/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="<div class='a'></div><div class='a i'></div>",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="<select msallowclip=''><option selected=''></option></select>",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=lb(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=mb(b);function pb(){}pb.prototype=d.filters=d.pseudos,d.setFilters=new pb,g=fb.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){(!c||(e=S.exec(h)))&&(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=T.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(R," ")}),h=h.slice(c.length));for(g in d.filter)!(e=X[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fb.error(a):z(a,i).slice(0)};function qb(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b)
},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=L.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var Q=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,R=["Top","Right","Bottom","Left"],S=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)},T=/^(?:checkbox|radio)$/i;!function(){var a=l.createDocumentFragment(),b=a.appendChild(l.createElement("div")),c=l.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,d,e,f=b.button;return null==a.pageX&&null!=b.clientX&&(c=a.target.ownerDocument||l,d=c.documentElement,e=c.body,a.pageX=b.clientX+(d&&d.scrollLeft||e&&e.scrollLeft||0)-(d&&d.clientLeft||e&&e.clientLeft||0),a.pageY=b.clientY+(d&&d.scrollTop||e&&e.scrollTop||0)-(d&&d.clientTop||e&&e.clientTop||0)),a.which||void 0===f||(a.which=1&f?1:2&f?3:4&f?2:0),a}},fix:function(a){if(a[n.expando])return a;var b,c,d,e=a.type,f=a,g=this.fixHooks[e];g||(this.fixHooks[e]=g=W.test(e)?this.mouseHooks:V.test(e)?this.keyHooks:{}),d=g.props?this.props.concat(g.props):this.props,a=new n.Event(f),b=d.length;while(b--)c=d[b],a[c]=f[c];return a.target||(a.target=l),3===a.target.nodeType&&(a.target=a.target.parentNode),g.filter?g.filter(a,f):a},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==_()&&this.focus?(this.focus(),!1):void 0},delegateType:"focusin"},blur:{trigger:function(){return this===_()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&n.nodeName(this,"input")?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c,d){var e=n.extend(new n.Event,c,{type:a,isSimulated:!0,originalEvent:{}});d?n.event.trigger(e,null,b):n.event.dispatch.call(b,e),e.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?Z:$):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={isDefaultPrevented:$,isPropagationStopped:$,isImmediatePropagationStopped:$,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=Z,a&&a.preventDefault&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=Z,a&&a.stopPropagation&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=Z,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return(!e||e!==d&&!n.contains(d,e))&&(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),k.focusinBubbles||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a),!0)};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=L.access(d,b);e||d.addEventListener(a,c,!0),L.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=L.access(d,b)-1;e?L.access(d,b,e):(d.removeEventListener(a,c,!0),L.remove(d,b))}}}),n.fn.extend({on:function(a,b,c,d,e){var f,g;if("object"==typeof a){"string"!=typeof b&&(c=c||b,b=void 0);for(g in a)this.on(g,b,c,a[g],e);return this}if(null==c&&null==d?(d=b,c=b=void 0):null==d&&("string"==typeof b?(d=c,c=void 0):(d=c,c=b,b=void 0)),d===!1)d=$;else if(!d)return this;return 1===e&&(f=d,d=function(a){return n().off(a),f.apply(this,arguments)},d.guid=f.guid||(f.guid=n.guid++)),this.each(function(){n.event.add(this,a,d,c,b)})},one:function(a,b,c,d){return this.on(a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return(b===!1||"function"==typeof b)&&(c=b,b=void 0),c===!1&&(c=$),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ab=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,ib={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1></$2>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1></$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=qb[0].contentDocument,b.write(),b.close(),c=sb(a,b),qb.detach()),rb[a]=c),c}var ub=/^margin/,vb=new RegExp("^("+Q+")(?!px)[a-z%]+$","i"),wb=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)};function xb(a,b,c){var d,e,f,g,h=a.style;return c=c||wb(a),c&&(g=c.getPropertyValue(b)||c[b]),c&&(""!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),vb.test(g)&&ub.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function yb(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d=l.documentElement,e=l.createElement("div"),f=l.createElement("div");if(f.style){f.style.backgroundClip="content-box",f.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===f.style.backgroundClip,e.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",e.appendChild(f);function g(){f.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",f.innerHTML="",d.appendChild(e);var g=a.getComputedStyle(f,null);b="1%"!==g.top,c="4px"===g.width,d.removeChild(e)}a.getComputedStyle&&n.extend(k,{pixelPosition:function(){return g(),b},boxSizingReliable:function(){return null==c&&g(),c},reliableMarginRight:function(){var b,c=f.appendChild(l.createElement("div"));return c.style.cssText=f.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",c.style.marginRight=c.style.width="0",f.style.width="1px",d.appendChild(e),b=!parseFloat(a.getComputedStyle(c,null).marginRight),d.removeChild(e),b}})}}(),n.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var zb=/^(none|table(?!-c[ea]).+)/,Ab=new RegExp("^("+Q+")(.*)$","i"),Bb=new RegExp("^([+-])=("+Q+")","i"),Cb={position:"absolute",visibility:"hidden",display:"block"},Db={letterSpacing:"0",fontWeight:"400"},Eb=["Webkit","O","Moz","ms"];function Fb(a,b){if(b in a)return b;var c=b[0].toUpperCase()+b.slice(1),d=b,e=Eb.length;while(e--)if(b=Eb[e]+c,b in a)return b;return d}function Gb(a,b,c){var d=Ab.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Hb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+R[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+R[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+R[f]+"Width",!0,e))):(g+=n.css(a,"padding"+R[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+R[f]+"Width",!0,e)));return g}function Ib(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=wb(a),g="border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=xb(a,b,f),(0>e||null==e)&&(e=a.style[b]),vb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Hb(a,b,c||(g?"border":"content"),d,f)+"px"}function Jb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=L.get(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&S(d)&&(f[g]=L.access(d,"olddisplay",tb(d.nodeName)))):(e=S(d),"none"===c&&e||L.set(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=xb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;return b=n.cssProps[h]||(n.cssProps[h]=Fb(i,h)),g=n.cssHooks[b]||n.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=Bb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(n.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||n.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=Fb(a.style,h)),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=xb(a,b,d)),"normal"===e&&b in Db&&(e=Db[b]),""===c||c?(f=parseFloat(e),c===!0||n.isNumeric(f)?f||0:e):e}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?zb.test(n.css(a,"display"))&&0===a.offsetWidth?n.swap(a,Cb,function(){return Ib(a,b,d)}):Ib(a,b,d):void 0},set:function(a,c,d){var e=d&&wb(a);return Gb(a,c,d?Hb(a,b,d,"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),n.cssHooks.marginRight=yb(k.reliableMarginRight,function(a,b){return b?n.swap(a,{display:"inline-block"},xb,[a,"marginRight"]):void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+R[d]+b]=f[d]||f[d-2]||f[0];return e}},ub.test(a)||(n.cssHooks[a+b].set=Gb)}),n.fn.extend({css:function(a,b){return J(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=wb(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return Jb(this,!0)},hide:function(){return Jb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){S(this)?n(this).show():n(this).hide()})}});function Kb(a,b,c,d,e){return new Kb.prototype.init(a,b,c,d,e)}n.Tween=Kb,Kb.prototype={constructor:Kb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=Kb.propHooks[this.prop];return a&&a.get?a.get(this):Kb.propHooks._default.get(this)},run:function(a){var b,c=Kb.propHooks[this.prop];return this.pos=b=this.options.duration?n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Kb.propHooks._default.set(this),this}},Kb.prototype.init.prototype=Kb.prototype,Kb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[n.cssProps[a.prop]]||n.cssHooks[a.prop])?n.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Kb.propHooks.scrollTop=Kb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},n.fx=Kb.prototype.init,n.fx.step={};var Lb,Mb,Nb=/^(?:toggle|show|hide)$/,Ob=new RegExp("^(?:([+-])=|)("+Q+")([a-z%]*)$","i"),Pb=/queueHooks$/,Qb=[Vb],Rb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=Ob.exec(b),f=e&&e[3]||(n.cssNumber[a]?"":"px"),g=(n.cssNumber[a]||"px"!==f&&+d)&&Ob.exec(n.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,n.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function Sb(){return setTimeout(function(){Lb=void 0}),Lb=n.now()}function Tb(a,b){var c,d=0,e={height:a};for(b=b?1:0;4>d;d+=2-b)c=R[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function Ub(a,b,c){for(var d,e=(Rb[b]||[]).concat(Rb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function Vb(a,b,c){var d,e,f,g,h,i,j,k,l=this,m={},o=a.style,p=a.nodeType&&S(a),q=L.get(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,l.always(function(){l.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=n.css(a,"display"),k="none"===j?L.get(a,"olddisplay")||tb(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(o.display="inline-block")),c.overflow&&(o.overflow="hidden",l.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],Nb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}m[d]=q&&q[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(m))"inline"===("none"===j?tb(a.nodeName):j)&&(o.display=j);else{q?"hidden"in q&&(p=q.hidden):q=L.access(a,"fxshow",{}),f&&(q.hidden=!p),p?n(a).show():l.done(function(){n(a).hide()}),l.done(function(){var b;L.remove(a,"fxshow");for(b in m)n.style(a,b,m[b])});for(d in m)g=Ub(p?q[d]:0,d,l),d in q||(q[d]=g.start,p&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function Wb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function Xb(a,b,c){var d,e,f=0,g=Qb.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Lb||Sb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:Lb||Sb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(Wb(k,j.opts.specialEasing);g>f;f++)if(d=Qb[f].call(j,a,k,j.opts))return d;return n.map(k,Ub,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(Xb,{tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],Rb[c]=Rb[c]||[],Rb[c].unshift(b)},prefilter:function(a,b){b?Qb.unshift(a):Qb.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(S).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=Xb(this,n.extend({},a),f);(e||L.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=L.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&Pb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=L.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(Tb(b,!0),a,d,e)}}),n.each({slideDown:Tb("show"),slideUp:Tb("hide"),slideToggle:Tb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=0,c=n.timers;for(Lb=n.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||n.fx.stop(),Lb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){Mb||(Mb=setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){clearInterval(Mb),Mb=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(a,b){return a=n.fx?n.fx.speeds[a]||a:a,b=b||"fx",this.queue(b,function(b,c){var d=setTimeout(b,a);c.stop=function(){clearTimeout(d)}})},function(){var a=l.createElement("input"),b=l.createElement("select"),c=b.appendChild(l.createElement("option"));a.type="checkbox",k.checkOn=""!==a.value,k.optSelected=c.selected,b.disabled=!0,k.optDisabled=!c.disabled,a=l.createElement("input"),a.value="t",a.type="radio",k.radioValue="t"===a.value}();var Yb,Zb,$b=n.expr.attrHandle;n.fn.extend({attr:function(a,b){return J(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===U?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),d=n.attrHooks[b]||(n.expr.match.bool.test(b)?Zb:Yb)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=n.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void n.removeAttr(a,b))
},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)&&(a[d]=!1),a.removeAttribute(c)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),Zb={set:function(a,b,c){return b===!1?n.removeAttr(a,c):a.setAttribute(c,c),c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=$b[b]||n.find.attr;$b[b]=function(a,b,d){var e,f;return d||(f=$b[b],$b[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,$b[b]=f),e}});var _b=/^(?:input|select|textarea|button)$/i;n.fn.extend({prop:function(a,b){return J(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[n.propFix[a]||a]})}}),n.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!n.isXMLDoc(a),f&&(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){return a.hasAttribute("tabindex")||_b.test(a.nodeName)||a.href?a.tabIndex:-1}}}}),k.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this});var ac=/[\t\r\n\f]/g;n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h="string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=n.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0===arguments.length||"string"==typeof a&&a,i=0,j=this.length;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,this.className))});if(h)for(b=(a||"").match(E)||[];j>i;i++)if(c=this[i],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(ac," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?n.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(n.isFunction(a)?function(c){n(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=n(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===U||"boolean"===c)&&(this.className&&L.set(this,"__className__",this.className),this.className=this.className||a===!1?"":L.get(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(ac," ").indexOf(b)>=0)return!0;return!1}});var bc=/\r/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(bc,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=n.inArray(d.value,f)>=0)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>=0:void 0}},k.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var cc=n.now(),dc=/\?/;n.parseJSON=function(a){return JSON.parse(a+"")},n.parseXML=function(a){var b,c;if(!a||"string"!=typeof a)return null;try{c=new DOMParser,b=c.parseFromString(a,"text/xml")}catch(d){b=void 0}return(!b||b.getElementsByTagName("parsererror").length)&&n.error("Invalid XML: "+a),b};var ec,fc,gc=/#.*$/,hc=/([?&])_=[^&]*/,ic=/^(.*?):[ \t]*([^\r\n]*)$/gm,jc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,kc=/^(?:GET|HEAD)$/,lc=/^\/\//,mc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,nc={},oc={},pc="*/".concat("*");try{fc=location.href}catch(qc){fc=l.createElement("a"),fc.href="",fc=fc.href}ec=mc.exec(fc.toLowerCase())||[];function rc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(n.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function sc(a,b,c,d){var e={},f=a===oc;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function tc(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&n.extend(!0,a,d),a}function uc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function vc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:fc,type:"GET",isLocal:jc.test(ec[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":pc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?tc(tc(a,n.ajaxSettings),b):tc(n.ajaxSettings,a)},ajaxPrefilter:rc(nc),ajaxTransport:rc(oc),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=n.ajaxSetup({},b),l=k.context||k,m=k.context&&(l.nodeType||l.jquery)?n(l):n.event,o=n.Deferred(),p=n.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!f){f={};while(b=ic.exec(e))f[b[1].toLowerCase()]=b[2]}b=f[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?e:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return c&&c.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||fc)+"").replace(gc,"").replace(lc,ec[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=n.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(h=mc.exec(k.url.toLowerCase()),k.crossDomain=!(!h||h[1]===ec[1]&&h[2]===ec[2]&&(h[3]||("http:"===h[1]?"80":"443"))===(ec[3]||("http:"===ec[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=n.param(k.data,k.traditional)),sc(nc,k,b,v),2===t)return v;i=k.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!kc.test(k.type),d=k.url,k.hasContent||(k.data&&(d=k.url+=(dc.test(d)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=hc.test(d)?d.replace(hc,"$1_="+cc++):d+(dc.test(d)?"&":"?")+"_="+cc++)),k.ifModified&&(n.lastModified[d]&&v.setRequestHeader("If-Modified-Since",n.lastModified[d]),n.etag[d]&&v.setRequestHeader("If-None-Match",n.etag[d])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+pc+"; q=0.01":""):k.accepts["*"]);for(j in k.headers)v.setRequestHeader(j,k.headers[j]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(j in{success:1,error:1,complete:1})v[j](k[j]);if(c=sc(oc,k,b,v)){v.readyState=1,i&&m.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,c.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,f,h){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),c=void 0,e=h||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,f&&(u=uc(k,v,f)),u=vc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(n.lastModified[d]=w),w=v.getResponseHeader("etag"),w&&(n.etag[d]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,i&&m.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),i&&(m.trigger("ajaxComplete",[v,k]),--n.active||n.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){var b;return n.isFunction(a)?this.each(function(b){n(this).wrapAll(a.call(this,b))}):(this[0]&&(b=n(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this)},wrapInner:function(a){return this.each(n.isFunction(a)?function(b){n(this).wrapInner(a.call(this,b))}:function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}}),n.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var wc=/%20/g,xc=/\[\]$/,yc=/\r?\n/g,zc=/^(?:submit|button|image|reset|file)$/i,Ac=/^(?:input|select|textarea|keygen)/i;function Bc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||xc.test(a)?d(a,e):Bc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)Bc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)Bc(c,a[c],b,e);return d.join("&").replace(wc,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&Ac.test(this.nodeName)&&!zc.test(a)&&(this.checked||!T.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(yc,"\r\n")}}):{name:b.name,value:c.replace(yc,"\r\n")}}).get()}}),n.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(a){}};var Cc=0,Dc={},Ec={0:200,1223:204},Fc=n.ajaxSettings.xhr();a.ActiveXObject&&n(a).on("unload",function(){for(var a in Dc)Dc[a]()}),k.cors=!!Fc&&"withCredentials"in Fc,k.ajax=Fc=!!Fc,n.ajaxTransport(function(a){var b;return k.cors||Fc&&!a.crossDomain?{send:function(c,d){var e,f=a.xhr(),g=++Cc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)f.setRequestHeader(e,c[e]);b=function(a){return function(){b&&(delete Dc[g],b=f.onload=f.onerror=null,"abort"===a?f.abort():"error"===a?d(f.status,f.statusText):d(Ec[f.status]||f.status,f.statusText,"string"==typeof f.responseText?{text:f.responseText}:void 0,f.getAllResponseHeaders()))}},f.onload=b(),f.onerror=b("error"),b=Dc[g]=b("abort");try{f.send(a.hasContent&&a.data||null)}catch(h){if(b)throw h}},abort:function(){b&&b()}}:void 0}),n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(d,e){b=n("<script>").prop({async:!0,charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&e("error"===a.type?404:200,a.type)}),l.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Gc=[],Hc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Gc.pop()||n.expando+"_"+cc++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Hc.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Hc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Hc,"$1"+e):b.jsonp!==!1&&(b.url+=(dc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Gc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||l;var d=v.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=n.buildFragment([a],b,e),e&&e.length&&n(e).remove(),n.merge([],d.childNodes))};var Ic=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&Ic)return Ic.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=n.trim(a.slice(h)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e,dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,f||[a.responseText,b,a])}),this},n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};var Jc=a.document.documentElement;function Kc(a){return n.isWindow(a)?a:9===a.nodeType&&a.defaultView}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d=this[0],e={top:0,left:0},f=d&&d.ownerDocument;if(f)return b=f.documentElement,n.contains(b,d)?(typeof d.getBoundingClientRect!==U&&(e=d.getBoundingClientRect()),c=Kc(f),{top:e.top+c.pageYOffset-b.clientTop,left:e.left+c.pageXOffset-b.clientLeft}):e},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===n.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(d=a.offset()),d.top+=n.css(a[0],"borderTopWidth",!0),d.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-d.top-n.css(c,"marginTop",!0),left:b.left-d.left-n.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||Jc;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Jc})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(b,c){var d="pageYOffset"===c;n.fn[b]=function(e){return J(this,function(b,e,f){var g=Kc(b);return void 0===f?g?g[c]:b[e]:void(g?g.scrollTo(d?a.pageXOffset:f,d?f:a.pageYOffset):b[e]=f)},b,e,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=yb(k.pixelPosition,function(a,c){return c?(c=xb(a,b),vb.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return J(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var Lc=a.jQuery,Mc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=Mc),b&&a.jQuery===n&&(a.jQuery=Lc),n},typeof b===U&&(a.jQuery=a.$=n),n});
;
/*!
    localForage -- Offline Storage, Improved
    Version 1.10.0
    https://localforage.github.io/localForage
    (c) 2013-2017 Mozilla, Apache License 2.0
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.localforage = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw (f.code="MODULE_NOT_FOUND", f)}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
(function (global){
'use strict';
var Mutation = global.MutationObserver || global.WebKitMutationObserver;

var scheduleDrain;

{
  if (Mutation) {
    var called = 0;
    var observer = new Mutation(nextTick);
    var element = global.document.createTextNode('');
    observer.observe(element, {
      characterData: true
    });
    scheduleDrain = function () {
      element.data = (called = ++called % 2);
    };
  } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
    var channel = new global.MessageChannel();
    channel.port1.onmessage = nextTick;
    scheduleDrain = function () {
      channel.port2.postMessage(0);
    };
  } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
    scheduleDrain = function () {

      // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
      // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
      var scriptEl = global.document.createElement('script');
      scriptEl.onreadystatechange = function () {
        nextTick();

        scriptEl.onreadystatechange = null;
        scriptEl.parentNode.removeChild(scriptEl);
        scriptEl = null;
      };
      global.document.documentElement.appendChild(scriptEl);
    };
  } else {
    scheduleDrain = function () {
      setTimeout(nextTick, 0);
    };
  }
}

var draining;
var queue = [];
//named nextTick for less confusing stack traces
function nextTick() {
  draining = true;
  var i, oldQueue;
  var len = queue.length;
  while (len) {
    oldQueue = queue;
    queue = [];
    i = -1;
    while (++i < len) {
      oldQueue[i]();
    }
    len = queue.length;
  }
  draining = false;
}

module.exports = immediate;
function immediate(task) {
  if (queue.push(task) === 1 && !draining) {
    scheduleDrain();
  }
}

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],2:[function(_dereq_,module,exports){
'use strict';
var immediate = _dereq_(1);

/* istanbul ignore next */
function INTERNAL() {}

var handlers = {};

var REJECTED = ['REJECTED'];
var FULFILLED = ['FULFILLED'];
var PENDING = ['PENDING'];

module.exports = Promise;

function Promise(resolver) {
  if (typeof resolver !== 'function') {
    throw new TypeError('resolver must be a function');
  }
  this.state = PENDING;
  this.queue = [];
  this.outcome = void 0;
  if (resolver !== INTERNAL) {
    safelyResolveThenable(this, resolver);
  }
}

Promise.prototype["catch"] = function (onRejected) {
  return this.then(null, onRejected);
};
Promise.prototype.then = function (onFulfilled, onRejected) {
  if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
    typeof onRejected !== 'function' && this.state === REJECTED) {
    return this;
  }
  var promise = new this.constructor(INTERNAL);
  if (this.state !== PENDING) {
    var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
    unwrap(promise, resolver, this.outcome);
  } else {
    this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
  }

  return promise;
};
function QueueItem(promise, onFulfilled, onRejected) {
  this.promise = promise;
  if (typeof onFulfilled === 'function') {
    this.onFulfilled = onFulfilled;
    this.callFulfilled = this.otherCallFulfilled;
  }
  if (typeof onRejected === 'function') {
    this.onRejected = onRejected;
    this.callRejected = this.otherCallRejected;
  }
}
QueueItem.prototype.callFulfilled = function (value) {
  handlers.resolve(this.promise, value);
};
QueueItem.prototype.otherCallFulfilled = function (value) {
  unwrap(this.promise, this.onFulfilled, value);
};
QueueItem.prototype.callRejected = function (value) {
  handlers.reject(this.promise, value);
};
QueueItem.prototype.otherCallRejected = function (value) {
  unwrap(this.promise, this.onRejected, value);
};

function unwrap(promise, func, value) {
  immediate(function () {
    var returnValue;
    try {
      returnValue = func(value);
    } catch (e) {
      return handlers.reject(promise, e);
    }
    if (returnValue === promise) {
      handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
    } else {
      handlers.resolve(promise, returnValue);
    }
  });
}

handlers.resolve = function (self, value) {
  var result = tryCatch(getThen, value);
  if (result.status === 'error') {
    return handlers.reject(self, result.value);
  }
  var thenable = result.value;

  if (thenable) {
    safelyResolveThenable(self, thenable);
  } else {
    self.state = FULFILLED;
    self.outcome = value;
    var i = -1;
    var len = self.queue.length;
    while (++i < len) {
      self.queue[i].callFulfilled(value);
    }
  }
  return self;
};
handlers.reject = function (self, error) {
  self.state = REJECTED;
  self.outcome = error;
  var i = -1;
  var len = self.queue.length;
  while (++i < len) {
    self.queue[i].callRejected(error);
  }
  return self;
};

function getThen(obj) {
  // Make sure we only access the accessor once as required by the spec
  var then = obj && obj.then;
  if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
    return function appyThen() {
      then.apply(obj, arguments);
    };
  }
}

function safelyResolveThenable(self, thenable) {
  // Either fulfill, reject or reject with error
  var called = false;
  function onError(value) {
    if (called) {
      return;
    }
    called = true;
    handlers.reject(self, value);
  }

  function onSuccess(value) {
    if (called) {
      return;
    }
    called = true;
    handlers.resolve(self, value);
  }

  function tryToUnwrap() {
    thenable(onSuccess, onError);
  }

  var result = tryCatch(tryToUnwrap);
  if (result.status === 'error') {
    onError(result.value);
  }
}

function tryCatch(func, value) {
  var out = {};
  try {
    out.value = func(value);
    out.status = 'success';
  } catch (e) {
    out.status = 'error';
    out.value = e;
  }
  return out;
}

Promise.resolve = resolve;
function resolve(value) {
  if (value instanceof this) {
    return value;
  }
  return handlers.resolve(new this(INTERNAL), value);
}

Promise.reject = reject;
function reject(reason) {
  var promise = new this(INTERNAL);
  return handlers.reject(promise, reason);
}

Promise.all = all;
function all(iterable) {
  var self = this;
  if (Object.prototype.toString.call(iterable) !== '[object Array]') {
    return this.reject(new TypeError('must be an array'));
  }

  var len = iterable.length;
  var called = false;
  if (!len) {
    return this.resolve([]);
  }

  var values = new Array(len);
  var resolved = 0;
  var i = -1;
  var promise = new this(INTERNAL);

  while (++i < len) {
    allResolver(iterable[i], i);
  }
  return promise;
  function allResolver(value, i) {
    self.resolve(value).then(resolveFromAll, function (error) {
      if (!called) {
        called = true;
        handlers.reject(promise, error);
      }
    });
    function resolveFromAll(outValue) {
      values[i] = outValue;
      if (++resolved === len && !called) {
        called = true;
        handlers.resolve(promise, values);
      }
    }
  }
}

Promise.race = race;
function race(iterable) {
  var self = this;
  if (Object.prototype.toString.call(iterable) !== '[object Array]') {
    return this.reject(new TypeError('must be an array'));
  }

  var len = iterable.length;
  var called = false;
  if (!len) {
    return this.resolve([]);
  }

  var i = -1;
  var promise = new this(INTERNAL);

  while (++i < len) {
    resolver(iterable[i]);
  }
  return promise;
  function resolver(value) {
    self.resolve(value).then(function (response) {
      if (!called) {
        called = true;
        handlers.resolve(promise, response);
      }
    }, function (error) {
      if (!called) {
        called = true;
        handlers.reject(promise, error);
      }
    });
  }
}

},{"1":1}],3:[function(_dereq_,module,exports){
(function (global){
'use strict';
if (typeof global.Promise !== 'function') {
  global.Promise = _dereq_(2);
}

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"2":2}],4:[function(_dereq_,module,exports){
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function getIDB() {
    /* global indexedDB,webkitIndexedDB,mozIndexedDB,OIndexedDB,msIndexedDB */
    try {
        if (typeof indexedDB !== 'undefined') {
            return indexedDB;
        }
        if (typeof webkitIndexedDB !== 'undefined') {
            return webkitIndexedDB;
        }
        if (typeof mozIndexedDB !== 'undefined') {
            return mozIndexedDB;
        }
        if (typeof OIndexedDB !== 'undefined') {
            return OIndexedDB;
        }
        if (typeof msIndexedDB !== 'undefined') {
            return msIndexedDB;
        }
    } catch (e) {
        return;
    }
}

var idb = getIDB();

function isIndexedDBValid() {
    try {
        // Initialize IndexedDB; fall back to vendor-prefixed versions
        // if needed.
        if (!idb || !idb.open) {
            return false;
        }
        // We mimic PouchDB here;
        //
        // We test for openDatabase because IE Mobile identifies itself
        // as Safari. Oh the lulz...
        var isSafari = typeof openDatabase !== 'undefined' && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);

        var hasFetch = typeof fetch === 'function' && fetch.toString().indexOf('[native code') !== -1;

        // Safari <10.1 does not meet our requirements for IDB support
        // (see: https://github.com/pouchdb/pouchdb/issues/5572).
        // Safari 10.1 shipped with fetch, we can use that to detect it.
        // Note: this creates issues with `window.fetch` polyfills and
        // overrides; see:
        // https://github.com/localForage/localForage/issues/856
        return (!isSafari || hasFetch) && typeof indexedDB !== 'undefined' &&
        // some outdated implementations of IDB that appear on Samsung
        // and HTC Android devices <4.4 are missing IDBKeyRange
        // See: https://github.com/mozilla/localForage/issues/128
        // See: https://github.com/mozilla/localForage/issues/272
        typeof IDBKeyRange !== 'undefined';
    } catch (e) {
        return false;
    }
}

// Abstracts constructing a Blob object, so it also works in older
// browsers that don't support the native Blob constructor. (i.e.
// old QtWebKit versions, at least).
// Abstracts constructing a Blob object, so it also works in older
// browsers that don't support the native Blob constructor. (i.e.
// old QtWebKit versions, at least).
function createBlob(parts, properties) {
    /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
    parts = parts || [];
    properties = properties || {};
    try {
        return new Blob(parts, properties);
    } catch (e) {
        if (e.name !== 'TypeError') {
            throw e;
        }
        var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder;
        var builder = new Builder();
        for (var i = 0; i < parts.length; i += 1) {
            builder.append(parts[i]);
        }
        return builder.getBlob(properties.type);
    }
}

// This is CommonJS because lie is an external dependency, so Rollup
// can just ignore it.
if (typeof Promise === 'undefined') {
    // In the "nopromises" build this will just throw if you don't have
    // a global promise object, but it would throw anyway later.
    _dereq_(3);
}
var Promise$1 = Promise;

function executeCallback(promise, callback) {
    if (callback) {
        promise.then(function (result) {
            callback(null, result);
        }, function (error) {
            callback(error);
        });
    }
}

function executeTwoCallbacks(promise, callback, errorCallback) {
    if (typeof callback === 'function') {
        promise.then(callback);
    }

    if (typeof errorCallback === 'function') {
        promise["catch"](errorCallback);
    }
}

function normalizeKey(key) {
    // Cast the key to a string, as that's all we can set as a key.
    if (typeof key !== 'string') {
        console.warn(key + ' used as a key, but it is not a string.');
        key = String(key);
    }

    return key;
}

function getCallback() {
    if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
        return arguments[arguments.length - 1];
    }
}

// Some code originally from async_storage.js in
// [Gaia](https://github.com/mozilla-b2g/gaia).

var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support';
var supportsBlobs = void 0;
var dbContexts = {};
var toString = Object.prototype.toString;

// Transaction Modes
var READ_ONLY = 'readonly';
var READ_WRITE = 'readwrite';

// Transform a binary string to an array buffer, because otherwise
// weird stuff happens when you try to work with the binary string directly.
// It is known.
// From http://stackoverflow.com/questions/14967647/ (continues on next line)
// encode-decode-image-with-base64-breaks-image (2013-04-21)
function _binStringToArrayBuffer(bin) {
    var length = bin.length;
    var buf = new ArrayBuffer(length);
    var arr = new Uint8Array(buf);
    for (var i = 0; i < length; i++) {
        arr[i] = bin.charCodeAt(i);
    }
    return buf;
}

//
// Blobs are not supported in all versions of IndexedDB, notably
// Chrome <37 and Android <5. In those versions, storing a blob will throw.
//
// Various other blob bugs exist in Chrome v37-42 (inclusive).
// Detecting them is expensive and confusing to users, and Chrome 37-42
// is at very low usage worldwide, so we do a hacky userAgent check instead.
//
// content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120
// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
//
// Code borrowed from PouchDB. See:
// https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js
//
function _checkBlobSupportWithoutCaching(idb) {
    return new Promise$1(function (resolve) {
        var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
        var blob = createBlob(['']);
        txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');

        txn.onabort = function (e) {
            // If the transaction aborts now its due to not being able to
            // write to the database, likely due to the disk being full
            e.preventDefault();
            e.stopPropagation();
            resolve(false);
        };

        txn.oncomplete = function () {
            var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
            var matchedEdge = navigator.userAgent.match(/Edge\//);
            // MS Edge pretends to be Chrome 42:
            // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx
            resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);
        };
    })["catch"](function () {
        return false; // error, so assume unsupported
    });
}

function _checkBlobSupport(idb) {
    if (typeof supportsBlobs === 'boolean') {
        return Promise$1.resolve(supportsBlobs);
    }
    return _checkBlobSupportWithoutCaching(idb).then(function (value) {
        supportsBlobs = value;
        return supportsBlobs;
    });
}

function _deferReadiness(dbInfo) {
    var dbContext = dbContexts[dbInfo.name];

    // Create a deferred object representing the current database operation.
    var deferredOperation = {};

    deferredOperation.promise = new Promise$1(function (resolve, reject) {
        deferredOperation.resolve = resolve;
        deferredOperation.reject = reject;
    });

    // Enqueue the deferred operation.
    dbContext.deferredOperations.push(deferredOperation);

    // Chain its promise to the database readiness.
    if (!dbContext.dbReady) {
        dbContext.dbReady = deferredOperation.promise;
    } else {
        dbContext.dbReady = dbContext.dbReady.then(function () {
            return deferredOperation.promise;
        });
    }
}

function _advanceReadiness(dbInfo) {
    var dbContext = dbContexts[dbInfo.name];

    // Dequeue a deferred operation.
    var deferredOperation = dbContext.deferredOperations.pop();

    // Resolve its promise (which is part of the database readiness
    // chain of promises).
    if (deferredOperation) {
        deferredOperation.resolve();
        return deferredOperation.promise;
    }
}

function _rejectReadiness(dbInfo, err) {
    var dbContext = dbContexts[dbInfo.name];

    // Dequeue a deferred operation.
    var deferredOperation = dbContext.deferredOperations.pop();

    // Reject its promise (which is part of the database readiness
    // chain of promises).
    if (deferredOperation) {
        deferredOperation.reject(err);
        return deferredOperation.promise;
    }
}

function _getConnection(dbInfo, upgradeNeeded) {
    return new Promise$1(function (resolve, reject) {
        dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();

        if (dbInfo.db) {
            if (upgradeNeeded) {
                _deferReadiness(dbInfo);
                dbInfo.db.close();
            } else {
                return resolve(dbInfo.db);
            }
        }

        var dbArgs = [dbInfo.name];

        if (upgradeNeeded) {
            dbArgs.push(dbInfo.version);
        }

        var openreq = idb.open.apply(idb, dbArgs);

        if (upgradeNeeded) {
            openreq.onupgradeneeded = function (e) {
                var db = openreq.result;
                try {
                    db.createObjectStore(dbInfo.storeName);
                    if (e.oldVersion <= 1) {
                        // Added when support for blob shims was added
                        db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
                    }
                } catch (ex) {
                    if (ex.name === 'ConstraintError') {
                        console.warn('The database "' + dbInfo.name + '"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.');
                    } else {
                        throw ex;
                    }
                }
            };
        }

        openreq.onerror = function (e) {
            e.preventDefault();
            reject(openreq.error);
        };

        openreq.onsuccess = function () {
            var db = openreq.result;
            db.onversionchange = function (e) {
                // Triggered when the database is modified (e.g. adding an objectStore) or
                // deleted (even when initiated by other sessions in different tabs).
                // Closing the connection here prevents those operations from being blocked.
                // If the database is accessed again later by this instance, the connection
                // will be reopened or the database recreated as needed.
                e.target.close();
            };
            resolve(db);
            _advanceReadiness(dbInfo);
        };
    });
}

function _getOriginalConnection(dbInfo) {
    return _getConnection(dbInfo, false);
}

function _getUpgradedConnection(dbInfo) {
    return _getConnection(dbInfo, true);
}

function _isUpgradeNeeded(dbInfo, defaultVersion) {
    if (!dbInfo.db) {
        return true;
    }

    var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
    var isDowngrade = dbInfo.version < dbInfo.db.version;
    var isUpgrade = dbInfo.version > dbInfo.db.version;

    if (isDowngrade) {
        // If the version is not the default one
        // then warn for impossible downgrade.
        if (dbInfo.version !== defaultVersion) {
            console.warn('The database "' + dbInfo.name + '"' + " can't be downgraded from version " + dbInfo.db.version + ' to version ' + dbInfo.version + '.');
        }
        // Align the versions to prevent errors.
        dbInfo.version = dbInfo.db.version;
    }

    if (isUpgrade || isNewStore) {
        // If the store is new then increment the version (if needed).
        // This will trigger an "upgradeneeded" event which is required
        // for creating a store.
        if (isNewStore) {
            var incVersion = dbInfo.db.version + 1;
            if (incVersion > dbInfo.version) {
                dbInfo.version = incVersion;
            }
        }

        return true;
    }

    return false;
}

// encode a blob for indexeddb engines that don't support blobs
function _encodeBlob(blob) {
    return new Promise$1(function (resolve, reject) {
        var reader = new FileReader();
        reader.onerror = reject;
        reader.onloadend = function (e) {
            var base64 = btoa(e.target.result || '');
            resolve({
                __local_forage_encoded_blob: true,
                data: base64,
                type: blob.type
            });
        };
        reader.readAsBinaryString(blob);
    });
}

// decode an encoded blob
function _decodeBlob(encodedBlob) {
    var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));
    return createBlob([arrayBuff], { type: encodedBlob.type });
}

// is this one of our fancy encoded blobs?
function _isEncodedBlob(value) {
    return value && value.__local_forage_encoded_blob;
}

// Specialize the default `ready()` function by making it dependent
// on the current database operations. Thus, the driver will be actually
// ready when it's been initialized (default) *and* there are no pending
// operations on the database (initiated by some other instances).
function _fullyReady(callback) {
    var self = this;

    var promise = self._initReady().then(function () {
        var dbContext = dbContexts[self._dbInfo.name];

        if (dbContext && dbContext.dbReady) {
            return dbContext.dbReady;
        }
    });

    executeTwoCallbacks(promise, callback, callback);
    return promise;
}

// Try to establish a new db connection to replace the
// current one which is broken (i.e. experiencing
// InvalidStateError while creating a transaction).
function _tryReconnect(dbInfo) {
    _deferReadiness(dbInfo);

    var dbContext = dbContexts[dbInfo.name];
    var forages = dbContext.forages;

    for (var i = 0; i < forages.length; i++) {
        var forage = forages[i];
        if (forage._dbInfo.db) {
            forage._dbInfo.db.close();
            forage._dbInfo.db = null;
        }
    }
    dbInfo.db = null;

    return _getOriginalConnection(dbInfo).then(function (db) {
        dbInfo.db = db;
        if (_isUpgradeNeeded(dbInfo)) {
            // Reopen the database for upgrading.
            return _getUpgradedConnection(dbInfo);
        }
        return db;
    }).then(function (db) {
        // store the latest db reference
        // in case the db was upgraded
        dbInfo.db = dbContext.db = db;
        for (var i = 0; i < forages.length; i++) {
            forages[i]._dbInfo.db = db;
        }
    })["catch"](function (err) {
        _rejectReadiness(dbInfo, err);
        throw err;
    });
}

// FF doesn't like Promises (micro-tasks) and IDDB store operations,
// so we have to do it with callbacks
function createTransaction(dbInfo, mode, callback, retries) {
    if (retries === undefined) {
        retries = 1;
    }

    try {
        var tx = dbInfo.db.transaction(dbInfo.storeName, mode);
        callback(null, tx);
    } catch (err) {
        if (retries > 0 && (!dbInfo.db || err.name === 'InvalidStateError' || err.name === 'NotFoundError')) {
            return Promise$1.resolve().then(function () {
                if (!dbInfo.db || err.name === 'NotFoundError' && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {
                    // increase the db version, to create the new ObjectStore
                    if (dbInfo.db) {
                        dbInfo.version = dbInfo.db.version + 1;
                    }
                    // Reopen the database for upgrading.
                    return _getUpgradedConnection(dbInfo);
                }
            }).then(function () {
                return _tryReconnect(dbInfo).then(function () {
                    createTransaction(dbInfo, mode, callback, retries - 1);
                });
            })["catch"](callback);
        }

        callback(err);
    }
}

function createDbContext() {
    return {
        // Running localForages sharing a database.
        forages: [],
        // Shared database.
        db: null,
        // Database readiness (promise).
        dbReady: null,
        // Deferred operations on the database.
        deferredOperations: []
    };
}

// Open the IndexedDB database (automatically creates one if one didn't
// previously exist), using any options set in the config.
function _initStorage(options) {
    var self = this;
    var dbInfo = {
        db: null
    };

    if (options) {
        for (var i in options) {
            dbInfo[i] = options[i];
        }
    }

    // Get the current context of the database;
    var dbContext = dbContexts[dbInfo.name];

    // ...or create a new context.
    if (!dbContext) {
        dbContext = createDbContext();
        // Register the new context in the global container.
        dbContexts[dbInfo.name] = dbContext;
    }

    // Register itself as a running localForage in the current context.
    dbContext.forages.push(self);

    // Replace the default `ready()` function with the specialized one.
    if (!self._initReady) {
        self._initReady = self.ready;
        self.ready = _fullyReady;
    }

    // Create an array of initialization states of the related localForages.
    var initPromises = [];

    function ignoreErrors() {
        // Don't handle errors here,
        // just makes sure related localForages aren't pending.
        return Promise$1.resolve();
    }

    for (var j = 0; j < dbContext.forages.length; j++) {
        var forage = dbContext.forages[j];
        if (forage !== self) {
            // Don't wait for itself...
            initPromises.push(forage._initReady()["catch"](ignoreErrors));
        }
    }

    // Take a snapshot of the related localForages.
    var forages = dbContext.forages.slice(0);

    // Initialize the connection process only when
    // all the related localForages aren't pending.
    return Promise$1.all(initPromises).then(function () {
        dbInfo.db = dbContext.db;
        // Get the connection or open a new one without upgrade.
        return _getOriginalConnection(dbInfo);
    }).then(function (db) {
        dbInfo.db = db;
        if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {
            // Reopen the database for upgrading.
            return _getUpgradedConnection(dbInfo);
        }
        return db;
    }).then(function (db) {
        dbInfo.db = dbContext.db = db;
        self._dbInfo = dbInfo;
        // Share the final connection amongst related localForages.
        for (var k = 0; k < forages.length; k++) {
            var forage = forages[k];
            if (forage !== self) {
                // Self is already up-to-date.
                forage._dbInfo.db = dbInfo.db;
                forage._dbInfo.version = dbInfo.version;
            }
        }
    });
}

function getItem(key, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    var req = store.get(key);

                    req.onsuccess = function () {
                        var value = req.result;
                        if (value === undefined) {
                            value = null;
                        }
                        if (_isEncodedBlob(value)) {
                            value = _decodeBlob(value);
                        }
                        resolve(value);
                    };

                    req.onerror = function () {
                        reject(req.error);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

// Iterate over all items stored in database.
function iterate(iterator, callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    var req = store.openCursor();
                    var iterationNumber = 1;

                    req.onsuccess = function () {
                        var cursor = req.result;

                        if (cursor) {
                            var value = cursor.value;
                            if (_isEncodedBlob(value)) {
                                value = _decodeBlob(value);
                            }
                            var result = iterator(value, cursor.key, iterationNumber++);

                            // when the iterator callback returns any
                            // (non-`undefined`) value, then we stop
                            // the iteration immediately
                            if (result !== void 0) {
                                resolve(result);
                            } else {
                                cursor["continue"]();
                            }
                        } else {
                            resolve();
                        }
                    };

                    req.onerror = function () {
                        reject(req.error);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);

    return promise;
}

function setItem(key, value, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = new Promise$1(function (resolve, reject) {
        var dbInfo;
        self.ready().then(function () {
            dbInfo = self._dbInfo;
            if (toString.call(value) === '[object Blob]') {
                return _checkBlobSupport(dbInfo.db).then(function (blobSupport) {
                    if (blobSupport) {
                        return value;
                    }
                    return _encodeBlob(value);
                });
            }
            return value;
        }).then(function (value) {
            createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);

                    // The reason we don't _save_ null is because IE 10 does
                    // not support saving the `null` type in IndexedDB. How
                    // ironic, given the bug below!
                    // See: https://github.com/mozilla/localForage/issues/161
                    if (value === null) {
                        value = undefined;
                    }

                    var req = store.put(value, key);

                    transaction.oncomplete = function () {
                        // Cast to undefined so the value passed to
                        // callback/promise is the same as what one would get out
                        // of `getItem()` later. This leads to some weirdness
                        // (setItem('foo', undefined) will return `null`), but
                        // it's not my fault localStorage is our baseline and that
                        // it's weird.
                        if (value === undefined) {
                            value = null;
                        }

                        resolve(value);
                    };
                    transaction.onabort = transaction.onerror = function () {
                        var err = req.error ? req.error : req.transaction.error;
                        reject(err);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function removeItem(key, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    // We use a Grunt task to make this safe for IE and some
                    // versions of Android (including those used by Cordova).
                    // Normally IE won't like `.delete()` and will insist on
                    // using `['delete']()`, but we have a build step that
                    // fixes this for us now.
                    var req = store["delete"](key);
                    transaction.oncomplete = function () {
                        resolve();
                    };

                    transaction.onerror = function () {
                        reject(req.error);
                    };

                    // The request will be also be aborted if we've exceeded our storage
                    // space.
                    transaction.onabort = function () {
                        var err = req.error ? req.error : req.transaction.error;
                        reject(err);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function clear(callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    var req = store.clear();

                    transaction.oncomplete = function () {
                        resolve();
                    };

                    transaction.onabort = transaction.onerror = function () {
                        var err = req.error ? req.error : req.transaction.error;
                        reject(err);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function length(callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    var req = store.count();

                    req.onsuccess = function () {
                        resolve(req.result);
                    };

                    req.onerror = function () {
                        reject(req.error);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function key(n, callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        if (n < 0) {
            resolve(null);

            return;
        }

        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    var advanced = false;
                    var req = store.openKeyCursor();

                    req.onsuccess = function () {
                        var cursor = req.result;
                        if (!cursor) {
                            // this means there weren't enough keys
                            resolve(null);

                            return;
                        }

                        if (n === 0) {
                            // We have the first key, return it if that's what they
                            // wanted.
                            resolve(cursor.key);
                        } else {
                            if (!advanced) {
                                // Otherwise, ask the cursor to skip ahead n
                                // records.
                                advanced = true;
                                cursor.advance(n);
                            } else {
                                // When we get here, we've got the nth key.
                                resolve(cursor.key);
                            }
                        }
                    };

                    req.onerror = function () {
                        reject(req.error);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function keys(callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
                if (err) {
                    return reject(err);
                }

                try {
                    var store = transaction.objectStore(self._dbInfo.storeName);
                    var req = store.openKeyCursor();
                    var keys = [];

                    req.onsuccess = function () {
                        var cursor = req.result;

                        if (!cursor) {
                            resolve(keys);
                            return;
                        }

                        keys.push(cursor.key);
                        cursor["continue"]();
                    };

                    req.onerror = function () {
                        reject(req.error);
                    };
                } catch (e) {
                    reject(e);
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function dropInstance(options, callback) {
    callback = getCallback.apply(this, arguments);

    var currentConfig = this.config();
    options = typeof options !== 'function' && options || {};
    if (!options.name) {
        options.name = options.name || currentConfig.name;
        options.storeName = options.storeName || currentConfig.storeName;
    }

    var self = this;
    var promise;
    if (!options.name) {
        promise = Promise$1.reject('Invalid arguments');
    } else {
        var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db;

        var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function (db) {
            var dbContext = dbContexts[options.name];
            var forages = dbContext.forages;
            dbContext.db = db;
            for (var i = 0; i < forages.length; i++) {
                forages[i]._dbInfo.db = db;
            }
            return db;
        });

        if (!options.storeName) {
            promise = dbPromise.then(function (db) {
                _deferReadiness(options);

                var dbContext = dbContexts[options.name];
                var forages = dbContext.forages;

                db.close();
                for (var i = 0; i < forages.length; i++) {
                    var forage = forages[i];
                    forage._dbInfo.db = null;
                }

                var dropDBPromise = new Promise$1(function (resolve, reject) {
                    var req = idb.deleteDatabase(options.name);

                    req.onerror = function () {
                        var db = req.result;
                        if (db) {
                            db.close();
                        }
                        reject(req.error);
                    };

                    req.onblocked = function () {
                        // Closing all open connections in onversionchange handler should prevent this situation, but if
                        // we do get here, it just means the request remains pending - eventually it will succeed or error
                        console.warn('dropInstance blocked for database "' + options.name + '" until all open connections are closed');
                    };

                    req.onsuccess = function () {
                        var db = req.result;
                        if (db) {
                            db.close();
                        }
                        resolve(db);
                    };
                });

                return dropDBPromise.then(function (db) {
                    dbContext.db = db;
                    for (var i = 0; i < forages.length; i++) {
                        var _forage = forages[i];
                        _advanceReadiness(_forage._dbInfo);
                    }
                })["catch"](function (err) {
                    (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
                    throw err;
                });
            });
        } else {
            promise = dbPromise.then(function (db) {
                if (!db.objectStoreNames.contains(options.storeName)) {
                    return;
                }

                var newVersion = db.version + 1;

                _deferReadiness(options);

                var dbContext = dbContexts[options.name];
                var forages = dbContext.forages;

                db.close();
                for (var i = 0; i < forages.length; i++) {
                    var forage = forages[i];
                    forage._dbInfo.db = null;
                    forage._dbInfo.version = newVersion;
                }

                var dropObjectPromise = new Promise$1(function (resolve, reject) {
                    var req = idb.open(options.name, newVersion);

                    req.onerror = function (err) {
                        var db = req.result;
                        db.close();
                        reject(err);
                    };

                    req.onupgradeneeded = function () {
                        var db = req.result;
                        db.deleteObjectStore(options.storeName);
                    };

                    req.onsuccess = function () {
                        var db = req.result;
                        db.close();
                        resolve(db);
                    };
                });

                return dropObjectPromise.then(function (db) {
                    dbContext.db = db;
                    for (var j = 0; j < forages.length; j++) {
                        var _forage2 = forages[j];
                        _forage2._dbInfo.db = db;
                        _advanceReadiness(_forage2._dbInfo);
                    }
                })["catch"](function (err) {
                    (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
                    throw err;
                });
            });
        }
    }

    executeCallback(promise, callback);
    return promise;
}

var asyncStorage = {
    _driver: 'asyncStorage',
    _initStorage: _initStorage,
    _support: isIndexedDBValid(),
    iterate: iterate,
    getItem: getItem,
    setItem: setItem,
    removeItem: removeItem,
    clear: clear,
    length: length,
    key: key,
    keys: keys,
    dropInstance: dropInstance
};

function isWebSQLValid() {
    return typeof openDatabase === 'function';
}

// Sadly, the best way to save binary data in WebSQL/localStorage is serializing
// it to Base64, so this is how we store it to prevent very strange errors with less
// verbose ways of binary <-> string data storage.
var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

var BLOB_TYPE_PREFIX = '~~local_forage_type~';
var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;

var SERIALIZED_MARKER = '__lfsc__:';
var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;

// OMG the serializations!
var TYPE_ARRAYBUFFER = 'arbf';
var TYPE_BLOB = 'blob';
var TYPE_INT8ARRAY = 'si08';
var TYPE_UINT8ARRAY = 'ui08';
var TYPE_UINT8CLAMPEDARRAY = 'uic8';
var TYPE_INT16ARRAY = 'si16';
var TYPE_INT32ARRAY = 'si32';
var TYPE_UINT16ARRAY = 'ur16';
var TYPE_UINT32ARRAY = 'ui32';
var TYPE_FLOAT32ARRAY = 'fl32';
var TYPE_FLOAT64ARRAY = 'fl64';
var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;

var toString$1 = Object.prototype.toString;

function stringToBuffer(serializedString) {
    // Fill the string into a ArrayBuffer.
    var bufferLength = serializedString.length * 0.75;
    var len = serializedString.length;
    var i;
    var p = 0;
    var encoded1, encoded2, encoded3, encoded4;

    if (serializedString[serializedString.length - 1] === '=') {
        bufferLength--;
        if (serializedString[serializedString.length - 2] === '=') {
            bufferLength--;
        }
    }

    var buffer = new ArrayBuffer(bufferLength);
    var bytes = new Uint8Array(buffer);

    for (i = 0; i < len; i += 4) {
        encoded1 = BASE_CHARS.indexOf(serializedString[i]);
        encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);
        encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);
        encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);

        /*jslint bitwise: true */
        bytes[p++] = encoded1 << 2 | encoded2 >> 4;
        bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
        bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
    }
    return buffer;
}

// Converts a buffer to a string to store, serialized, in the backend
// storage library.
function bufferToString(buffer) {
    // base64-arraybuffer
    var bytes = new Uint8Array(buffer);
    var base64String = '';
    var i;

    for (i = 0; i < bytes.length; i += 3) {
        /*jslint bitwise: true */
        base64String += BASE_CHARS[bytes[i] >> 2];
        base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
        base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
        base64String += BASE_CHARS[bytes[i + 2] & 63];
    }

    if (bytes.length % 3 === 2) {
        base64String = base64String.substring(0, base64String.length - 1) + '=';
    } else if (bytes.length % 3 === 1) {
        base64String = base64String.substring(0, base64String.length - 2) + '==';
    }

    return base64String;
}

// Serialize a value, afterwards executing a callback (which usually
// instructs the `setItem()` callback/promise to be executed). This is how
// we store binary data with localStorage.
function serialize(value, callback) {
    var valueType = '';
    if (value) {
        valueType = toString$1.call(value);
    }

    // Cannot use `value instanceof ArrayBuffer` or such here, as these
    // checks fail when running the tests using casper.js...
    //
    // TODO: See why those tests fail and use a better solution.
    if (value && (valueType === '[object ArrayBuffer]' || value.buffer && toString$1.call(value.buffer) === '[object ArrayBuffer]')) {
        // Convert binary arrays to a string and prefix the string with
        // a special marker.
        var buffer;
        var marker = SERIALIZED_MARKER;

        if (value instanceof ArrayBuffer) {
            buffer = value;
            marker += TYPE_ARRAYBUFFER;
        } else {
            buffer = value.buffer;

            if (valueType === '[object Int8Array]') {
                marker += TYPE_INT8ARRAY;
            } else if (valueType === '[object Uint8Array]') {
                marker += TYPE_UINT8ARRAY;
            } else if (valueType === '[object Uint8ClampedArray]') {
                marker += TYPE_UINT8CLAMPEDARRAY;
            } else if (valueType === '[object Int16Array]') {
                marker += TYPE_INT16ARRAY;
            } else if (valueType === '[object Uint16Array]') {
                marker += TYPE_UINT16ARRAY;
            } else if (valueType === '[object Int32Array]') {
                marker += TYPE_INT32ARRAY;
            } else if (valueType === '[object Uint32Array]') {
                marker += TYPE_UINT32ARRAY;
            } else if (valueType === '[object Float32Array]') {
                marker += TYPE_FLOAT32ARRAY;
            } else if (valueType === '[object Float64Array]') {
                marker += TYPE_FLOAT64ARRAY;
            } else {
                callback(new Error('Failed to get type for BinaryArray'));
            }
        }

        callback(marker + bufferToString(buffer));
    } else if (valueType === '[object Blob]') {
        // Conver the blob to a binaryArray and then to a string.
        var fileReader = new FileReader();

        fileReader.onload = function () {
            // Backwards-compatible prefix for the blob type.
            var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result);

            callback(SERIALIZED_MARKER + TYPE_BLOB + str);
        };

        fileReader.readAsArrayBuffer(value);
    } else {
        try {
            callback(JSON.stringify(value));
        } catch (e) {
            console.error("Couldn't convert value into a JSON string: ", value);

            callback(null, e);
        }
    }
}

// Deserialize data we've inserted into a value column/field. We place
// special markers into our strings to mark them as encoded; this isn't
// as nice as a meta field, but it's the only sane thing we can do whilst
// keeping localStorage support intact.
//
// Oftentimes this will just deserialize JSON content, but if we have a
// special marker (SERIALIZED_MARKER, defined above), we will extract
// some kind of arraybuffer/binary data/typed array out of the string.
function deserialize(value) {
    // If we haven't marked this string as being specially serialized (i.e.
    // something other than serialized JSON), we can just return it and be
    // done with it.
    if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {
        return JSON.parse(value);
    }

    // The following code deals with deserializing some kind of Blob or
    // TypedArray. First we separate out the type of data we're dealing
    // with from the data itself.
    var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
    var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);

    var blobType;
    // Backwards-compatible blob type serialization strategy.
    // DBs created with older versions of localForage will simply not have the blob type.
    if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {
        var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);
        blobType = matcher[1];
        serializedString = serializedString.substring(matcher[0].length);
    }
    var buffer = stringToBuffer(serializedString);

    // Return the right type based on the code/type set during
    // serialization.
    switch (type) {
        case TYPE_ARRAYBUFFER:
            return buffer;
        case TYPE_BLOB:
            return createBlob([buffer], { type: blobType });
        case TYPE_INT8ARRAY:
            return new Int8Array(buffer);
        case TYPE_UINT8ARRAY:
            return new Uint8Array(buffer);
        case TYPE_UINT8CLAMPEDARRAY:
            return new Uint8ClampedArray(buffer);
        case TYPE_INT16ARRAY:
            return new Int16Array(buffer);
        case TYPE_UINT16ARRAY:
            return new Uint16Array(buffer);
        case TYPE_INT32ARRAY:
            return new Int32Array(buffer);
        case TYPE_UINT32ARRAY:
            return new Uint32Array(buffer);
        case TYPE_FLOAT32ARRAY:
            return new Float32Array(buffer);
        case TYPE_FLOAT64ARRAY:
            return new Float64Array(buffer);
        default:
            throw new Error('Unkown type: ' + type);
    }
}

var localforageSerializer = {
    serialize: serialize,
    deserialize: deserialize,
    stringToBuffer: stringToBuffer,
    bufferToString: bufferToString
};

/*
 * Includes code from:
 *
 * base64-arraybuffer
 * https://github.com/niklasvh/base64-arraybuffer
 *
 * Copyright (c) 2012 Niklas von Hertzen
 * Licensed under the MIT license.
 */

function createDbTable(t, dbInfo, callback, errorCallback) {
    t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' ' + '(id INTEGER PRIMARY KEY, key unique, value)', [], callback, errorCallback);
}

// Open the WebSQL database (automatically creates one if one didn't
// previously exist), using any options set in the config.
function _initStorage$1(options) {
    var self = this;
    var dbInfo = {
        db: null
    };

    if (options) {
        for (var i in options) {
            dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i];
        }
    }

    var dbInfoPromise = new Promise$1(function (resolve, reject) {
        // Open the database; the openDatabase API will automatically
        // create it for us if it doesn't exist.
        try {
            dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);
        } catch (e) {
            return reject(e);
        }

        // Create our key/value table if it doesn't exist.
        dbInfo.db.transaction(function (t) {
            createDbTable(t, dbInfo, function () {
                self._dbInfo = dbInfo;
                resolve();
            }, function (t, error) {
                reject(error);
            });
        }, reject);
    });

    dbInfo.serializer = localforageSerializer;
    return dbInfoPromise;
}

function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
    t.executeSql(sqlStatement, args, callback, function (t, error) {
        if (error.code === error.SYNTAX_ERR) {
            t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name = ?", [dbInfo.storeName], function (t, results) {
                if (!results.rows.length) {
                    // if the table is missing (was deleted)
                    // re-create it table and retry
                    createDbTable(t, dbInfo, function () {
                        t.executeSql(sqlStatement, args, callback, errorCallback);
                    }, errorCallback);
                } else {
                    errorCallback(t, error);
                }
            }, errorCallback);
        } else {
            errorCallback(t, error);
        }
    }, errorCallback);
}

function getItem$1(key, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;
            dbInfo.db.transaction(function (t) {
                tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) {
                    var result = results.rows.length ? results.rows.item(0).value : null;

                    // Check to see if this is serialized content we need to
                    // unpack.
                    if (result) {
                        result = dbInfo.serializer.deserialize(result);
                    }

                    resolve(result);
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function iterate$1(iterator, callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;

            dbInfo.db.transaction(function (t) {
                tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName, [], function (t, results) {
                    var rows = results.rows;
                    var length = rows.length;

                    for (var i = 0; i < length; i++) {
                        var item = rows.item(i);
                        var result = item.value;

                        // Check to see if this is serialized content
                        // we need to unpack.
                        if (result) {
                            result = dbInfo.serializer.deserialize(result);
                        }

                        result = iterator(result, item.key, i + 1);

                        // void(0) prevents problems with redefinition
                        // of `undefined`.
                        if (result !== void 0) {
                            resolve(result);
                            return;
                        }
                    }

                    resolve();
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function _setItem(key, value, callback, retriesLeft) {
    var self = this;

    key = normalizeKey(key);

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            // The localStorage API doesn't return undefined values in an
            // "expected" way, so undefined is always cast to null in all
            // drivers. See: https://github.com/mozilla/localForage/pull/42
            if (value === undefined) {
                value = null;
            }

            // Save the original value to pass to the callback.
            var originalValue = value;

            var dbInfo = self._dbInfo;
            dbInfo.serializer.serialize(value, function (value, error) {
                if (error) {
                    reject(error);
                } else {
                    dbInfo.db.transaction(function (t) {
                        tryExecuteSql(t, dbInfo, 'INSERT OR REPLACE INTO ' + dbInfo.storeName + ' ' + '(key, value) VALUES (?, ?)', [key, value], function () {
                            resolve(originalValue);
                        }, function (t, error) {
                            reject(error);
                        });
                    }, function (sqlError) {
                        // The transaction failed; check
                        // to see if it's a quota error.
                        if (sqlError.code === sqlError.QUOTA_ERR) {
                            // We reject the callback outright for now, but
                            // it's worth trying to re-run the transaction.
                            // Even if the user accepts the prompt to use
                            // more storage on Safari, this error will
                            // be called.
                            //
                            // Try to re-run the transaction.
                            if (retriesLeft > 0) {
                                resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
                                return;
                            }
                            reject(sqlError);
                        }
                    });
                }
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function setItem$1(key, value, callback) {
    return _setItem.apply(this, [key, value, callback, 1]);
}

function removeItem$1(key, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;
            dbInfo.db.transaction(function (t) {
                tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () {
                    resolve();
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

// Deletes every item in the table.
// TODO: Find out if this resets the AUTO_INCREMENT number.
function clear$1(callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;
            dbInfo.db.transaction(function (t) {
                tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName, [], function () {
                    resolve();
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

// Does a simple `COUNT(key)` to get the number of items stored in
// localForage.
function length$1(callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;
            dbInfo.db.transaction(function (t) {
                // Ahhh, SQL makes this one soooooo easy.
                tryExecuteSql(t, dbInfo, 'SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) {
                    var result = results.rows.item(0).c;
                    resolve(result);
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

// Return the key located at key index X; essentially gets the key from a
// `WHERE id = ?`. This is the most efficient way I can think to implement
// this rarely-used (in my experience) part of the API, but it can seem
// inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so
// the ID of each key will change every time it's updated. Perhaps a stored
// procedure for the `setItem()` SQL would solve this problem?
// TODO: Don't change ID on `setItem()`.
function key$1(n, callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;
            dbInfo.db.transaction(function (t) {
                tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) {
                    var result = results.rows.length ? results.rows.item(0).key : null;
                    resolve(result);
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

function keys$1(callback) {
    var self = this;

    var promise = new Promise$1(function (resolve, reject) {
        self.ready().then(function () {
            var dbInfo = self._dbInfo;
            dbInfo.db.transaction(function (t) {
                tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName, [], function (t, results) {
                    var keys = [];

                    for (var i = 0; i < results.rows.length; i++) {
                        keys.push(results.rows.item(i).key);
                    }

                    resolve(keys);
                }, function (t, error) {
                    reject(error);
                });
            });
        })["catch"](reject);
    });

    executeCallback(promise, callback);
    return promise;
}

// https://www.w3.org/TR/webdatabase/#databases
// > There is no way to enumerate or delete the databases available for an origin from this API.
function getAllStoreNames(db) {
    return new Promise$1(function (resolve, reject) {
        db.transaction(function (t) {
            t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function (t, results) {
                var storeNames = [];

                for (var i = 0; i < results.rows.length; i++) {
                    storeNames.push(results.rows.item(i).name);
                }

                resolve({
                    db: db,
                    storeNames: storeNames
                });
            }, function (t, error) {
                reject(error);
            });
        }, function (sqlError) {
            reject(sqlError);
        });
    });
}

function dropInstance$1(options, callback) {
    callback = getCallback.apply(this, arguments);

    var currentConfig = this.config();
    options = typeof options !== 'function' && options || {};
    if (!options.name) {
        options.name = options.name || currentConfig.name;
        options.storeName = options.storeName || currentConfig.storeName;
    }

    var self = this;
    var promise;
    if (!options.name) {
        promise = Promise$1.reject('Invalid arguments');
    } else {
        promise = new Promise$1(function (resolve) {
            var db;
            if (options.name === currentConfig.name) {
                // use the db reference of the current instance
                db = self._dbInfo.db;
            } else {
                db = openDatabase(options.name, '', '', 0);
            }

            if (!options.storeName) {
                // drop all database tables
                resolve(getAllStoreNames(db));
            } else {
                resolve({
                    db: db,
                    storeNames: [options.storeName]
                });
            }
        }).then(function (operationInfo) {
            return new Promise$1(function (resolve, reject) {
                operationInfo.db.transaction(function (t) {
                    function dropTable(storeName) {
                        return new Promise$1(function (resolve, reject) {
                            t.executeSql('DROP TABLE IF EXISTS ' + storeName, [], function () {
                                resolve();
                            }, function (t, error) {
                                reject(error);
                            });
                        });
                    }

                    var operations = [];
                    for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) {
                        operations.push(dropTable(operationInfo.storeNames[i]));
                    }

                    Promise$1.all(operations).then(function () {
                        resolve();
                    })["catch"](function (e) {
                        reject(e);
                    });
                }, function (sqlError) {
                    reject(sqlError);
                });
            });
        });
    }

    executeCallback(promise, callback);
    return promise;
}

var webSQLStorage = {
    _driver: 'webSQLStorage',
    _initStorage: _initStorage$1,
    _support: isWebSQLValid(),
    iterate: iterate$1,
    getItem: getItem$1,
    setItem: setItem$1,
    removeItem: removeItem$1,
    clear: clear$1,
    length: length$1,
    key: key$1,
    keys: keys$1,
    dropInstance: dropInstance$1
};

function isLocalStorageValid() {
    try {
        return typeof localStorage !== 'undefined' && 'setItem' in localStorage &&
        // in IE8 typeof localStorage.setItem === 'object'
        !!localStorage.setItem;
    } catch (e) {
        return false;
    }
}

function _getKeyPrefix(options, defaultConfig) {
    var keyPrefix = options.name + '/';

    if (options.storeName !== defaultConfig.storeName) {
        keyPrefix += options.storeName + '/';
    }
    return keyPrefix;
}

// Check if localStorage throws when saving an item
function checkIfLocalStorageThrows() {
    var localStorageTestKey = '_localforage_support_test';

    try {
        localStorage.setItem(localStorageTestKey, true);
        localStorage.removeItem(localStorageTestKey);

        return false;
    } catch (e) {
        return true;
    }
}

// Check if localStorage is usable and allows to save an item
// This method checks if localStorage is usable in Safari Private Browsing
// mode, or in any other case where the available quota for localStorage
// is 0 and there wasn't any saved items yet.
function _isLocalStorageUsable() {
    return !checkIfLocalStorageThrows() || localStorage.length > 0;
}

// Config the localStorage backend, using options set in the config.
function _initStorage$2(options) {
    var self = this;
    var dbInfo = {};
    if (options) {
        for (var i in options) {
            dbInfo[i] = options[i];
        }
    }

    dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);

    if (!_isLocalStorageUsable()) {
        return Promise$1.reject();
    }

    self._dbInfo = dbInfo;
    dbInfo.serializer = localforageSerializer;

    return Promise$1.resolve();
}

// Remove all keys from the datastore, effectively destroying all data in
// the app's key/value store!
function clear$2(callback) {
    var self = this;
    var promise = self.ready().then(function () {
        var keyPrefix = self._dbInfo.keyPrefix;

        for (var i = localStorage.length - 1; i >= 0; i--) {
            var key = localStorage.key(i);

            if (key.indexOf(keyPrefix) === 0) {
                localStorage.removeItem(key);
            }
        }
    });

    executeCallback(promise, callback);
    return promise;
}

// Retrieve an item from the store. Unlike the original async_storage
// library in Gaia, we don't modify return values at all. If a key's value
// is `undefined`, we pass that value to the callback function.
function getItem$2(key, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = self.ready().then(function () {
        var dbInfo = self._dbInfo;
        var result = localStorage.getItem(dbInfo.keyPrefix + key);

        // If a result was found, parse it from the serialized
        // string into a JS object. If result isn't truthy, the key
        // is likely undefined and we'll pass it straight to the
        // callback.
        if (result) {
            result = dbInfo.serializer.deserialize(result);
        }

        return result;
    });

    executeCallback(promise, callback);
    return promise;
}

// Iterate over all items in the store.
function iterate$2(iterator, callback) {
    var self = this;

    var promise = self.ready().then(function () {
        var dbInfo = self._dbInfo;
        var keyPrefix = dbInfo.keyPrefix;
        var keyPrefixLength = keyPrefix.length;
        var length = localStorage.length;

        // We use a dedicated iterator instead of the `i` variable below
        // so other keys we fetch in localStorage aren't counted in
        // the `iterationNumber` argument passed to the `iterate()`
        // callback.
        //
        // See: github.com/mozilla/localForage/pull/435#discussion_r38061530
        var iterationNumber = 1;

        for (var i = 0; i < length; i++) {
            var key = localStorage.key(i);
            if (key.indexOf(keyPrefix) !== 0) {
                continue;
            }
            var value = localStorage.getItem(key);

            // If a result was found, parse it from the serialized
            // string into a JS object. If result isn't truthy, the
            // key is likely undefined and we'll pass it straight
            // to the iterator.
            if (value) {
                value = dbInfo.serializer.deserialize(value);
            }

            value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);

            if (value !== void 0) {
                return value;
            }
        }
    });

    executeCallback(promise, callback);
    return promise;
}

// Same as localStorage's key() method, except takes a callback.
function key$2(n, callback) {
    var self = this;
    var promise = self.ready().then(function () {
        var dbInfo = self._dbInfo;
        var result;
        try {
            result = localStorage.key(n);
        } catch (error) {
            result = null;
        }

        // Remove the prefix from the key, if a key is found.
        if (result) {
            result = result.substring(dbInfo.keyPrefix.length);
        }

        return result;
    });

    executeCallback(promise, callback);
    return promise;
}

function keys$2(callback) {
    var self = this;
    var promise = self.ready().then(function () {
        var dbInfo = self._dbInfo;
        var length = localStorage.length;
        var keys = [];

        for (var i = 0; i < length; i++) {
            var itemKey = localStorage.key(i);
            if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {
                keys.push(itemKey.substring(dbInfo.keyPrefix.length));
            }
        }

        return keys;
    });

    executeCallback(promise, callback);
    return promise;
}

// Supply the number of keys in the datastore to the callback function.
function length$2(callback) {
    var self = this;
    var promise = self.keys().then(function (keys) {
        return keys.length;
    });

    executeCallback(promise, callback);
    return promise;
}

// Remove an item from the store, nice and simple.
function removeItem$2(key, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = self.ready().then(function () {
        var dbInfo = self._dbInfo;
        localStorage.removeItem(dbInfo.keyPrefix + key);
    });

    executeCallback(promise, callback);
    return promise;
}

// Set a key's value and run an optional callback once the value is set.
// Unlike Gaia's implementation, the callback function is passed the value,
// in case you want to operate on that value only after you're sure it
// saved, or something like that.
function setItem$2(key, value, callback) {
    var self = this;

    key = normalizeKey(key);

    var promise = self.ready().then(function () {
        // Convert undefined values to null.
        // https://github.com/mozilla/localForage/pull/42
        if (value === undefined) {
            value = null;
        }

        // Save the original value to pass to the callback.
        var originalValue = value;

        return new Promise$1(function (resolve, reject) {
            var dbInfo = self._dbInfo;
            dbInfo.serializer.serialize(value, function (value, error) {
                if (error) {
                    reject(error);
                } else {
                    try {
                        localStorage.setItem(dbInfo.keyPrefix + key, value);
                        resolve(originalValue);
                    } catch (e) {
                        // localStorage capacity exceeded.
                        // TODO: Make this a specific error/event.
                        if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
                            reject(e);
                        }
                        reject(e);
                    }
                }
            });
        });
    });

    executeCallback(promise, callback);
    return promise;
}

function dropInstance$2(options, callback) {
    callback = getCallback.apply(this, arguments);

    options = typeof options !== 'function' && options || {};
    if (!options.name) {
        var currentConfig = this.config();
        options.name = options.name || currentConfig.name;
        options.storeName = options.storeName || currentConfig.storeName;
    }

    var self = this;
    var promise;
    if (!options.name) {
        promise = Promise$1.reject('Invalid arguments');
    } else {
        promise = new Promise$1(function (resolve) {
            if (!options.storeName) {
                resolve(options.name + '/');
            } else {
                resolve(_getKeyPrefix(options, self._defaultConfig));
            }
        }).then(function (keyPrefix) {
            for (var i = localStorage.length - 1; i >= 0; i--) {
                var key = localStorage.key(i);

                if (key.indexOf(keyPrefix) === 0) {
                    localStorage.removeItem(key);
                }
            }
        });
    }

    executeCallback(promise, callback);
    return promise;
}

var localStorageWrapper = {
    _driver: 'localStorageWrapper',
    _initStorage: _initStorage$2,
    _support: isLocalStorageValid(),
    iterate: iterate$2,
    getItem: getItem$2,
    setItem: setItem$2,
    removeItem: removeItem$2,
    clear: clear$2,
    length: length$2,
    key: key$2,
    keys: keys$2,
    dropInstance: dropInstance$2
};

var sameValue = function sameValue(x, y) {
    return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y);
};

var includes = function includes(array, searchElement) {
    var len = array.length;
    var i = 0;
    while (i < len) {
        if (sameValue(array[i], searchElement)) {
            return true;
        }
        i++;
    }

    return false;
};

var isArray = Array.isArray || function (arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
};

// Drivers are stored here when `defineDriver()` is called.
// They are shared across all instances of localForage.
var DefinedDrivers = {};

var DriverSupport = {};

var DefaultDrivers = {
    INDEXEDDB: asyncStorage,
    WEBSQL: webSQLStorage,
    LOCALSTORAGE: localStorageWrapper
};

var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver];

var OptionalDriverMethods = ['dropInstance'];

var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods);

var DefaultConfig = {
    description: '',
    driver: DefaultDriverOrder.slice(),
    name: 'localforage',
    // Default DB size is _JUST UNDER_ 5MB, as it's the highest size
    // we can use without a prompt.
    size: 4980736,
    storeName: 'keyvaluepairs',
    version: 1.0
};

function callWhenReady(localForageInstance, libraryMethod) {
    localForageInstance[libraryMethod] = function () {
        var _args = arguments;
        return localForageInstance.ready().then(function () {
            return localForageInstance[libraryMethod].apply(localForageInstance, _args);
        });
    };
}

function extend() {
    for (var i = 1; i < arguments.length; i++) {
        var arg = arguments[i];

        if (arg) {
            for (var _key in arg) {
                if (arg.hasOwnProperty(_key)) {
                    if (isArray(arg[_key])) {
                        arguments[0][_key] = arg[_key].slice();
                    } else {
                        arguments[0][_key] = arg[_key];
                    }
                }
            }
        }
    }

    return arguments[0];
}

var LocalForage = function () {
    function LocalForage(options) {
        _classCallCheck(this, LocalForage);

        for (var driverTypeKey in DefaultDrivers) {
            if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {
                var driver = DefaultDrivers[driverTypeKey];
                var driverName = driver._driver;
                this[driverTypeKey] = driverName;

                if (!DefinedDrivers[driverName]) {
                    // we don't need to wait for the promise,
                    // since the default drivers can be defined
                    // in a blocking manner
                    this.defineDriver(driver);
                }
            }
        }

        this._defaultConfig = extend({}, DefaultConfig);
        this._config = extend({}, this._defaultConfig, options);
        this._driverSet = null;
        this._initDriver = null;
        this._ready = false;
        this._dbInfo = null;

        this._wrapLibraryMethodsWithReady();
        this.setDriver(this._config.driver)["catch"](function () {});
    }

    // Set any config values for localForage; can be called anytime before
    // the first API call (e.g. `getItem`, `setItem`).
    // We loop through options so we don't overwrite existing config
    // values.


    LocalForage.prototype.config = function config(options) {
        // If the options argument is an object, we use it to set values.
        // Otherwise, we return either a specified config value or all
        // config values.
        if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
            // If localforage is ready and fully initialized, we can't set
            // any new configuration values. Instead, we return an error.
            if (this._ready) {
                return new Error("Can't call config() after localforage " + 'has been used.');
            }

            for (var i in options) {
                if (i === 'storeName') {
                    options[i] = options[i].replace(/\W/g, '_');
                }

                if (i === 'version' && typeof options[i] !== 'number') {
                    return new Error('Database version must be a number.');
                }

                this._config[i] = options[i];
            }

            // after all config options are set and
            // the driver option is used, try setting it
            if ('driver' in options && options.driver) {
                return this.setDriver(this._config.driver);
            }

            return true;
        } else if (typeof options === 'string') {
            return this._config[options];
        } else {
            return this._config;
        }
    };

    // Used to define a custom driver, shared across all instances of
    // localForage.


    LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {
        var promise = new Promise$1(function (resolve, reject) {
            try {
                var driverName = driverObject._driver;
                var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver');

                // A driver name should be defined and not overlap with the
                // library-defined, default drivers.
                if (!driverObject._driver) {
                    reject(complianceError);
                    return;
                }

                var driverMethods = LibraryMethods.concat('_initStorage');
                for (var i = 0, len = driverMethods.length; i < len; i++) {
                    var driverMethodName = driverMethods[i];

                    // when the property is there,
                    // it should be a method even when optional
                    var isRequired = !includes(OptionalDriverMethods, driverMethodName);
                    if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== 'function') {
                        reject(complianceError);
                        return;
                    }
                }

                var configureMissingMethods = function configureMissingMethods() {
                    var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {
                        return function () {
                            var error = new Error('Method ' + methodName + ' is not implemented by the current driver');
                            var promise = Promise$1.reject(error);
                            executeCallback(promise, arguments[arguments.length - 1]);
                            return promise;
                        };
                    };

                    for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {
                        var optionalDriverMethod = OptionalDriverMethods[_i];
                        if (!driverObject[optionalDriverMethod]) {
                            driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);
                        }
                    }
                };

                configureMissingMethods();

                var setDriverSupport = function setDriverSupport(support) {
                    if (DefinedDrivers[driverName]) {
                        console.info('Redefining LocalForage driver: ' + driverName);
                    }
                    DefinedDrivers[driverName] = driverObject;
                    DriverSupport[driverName] = support;
                    // don't use a then, so that we can define
                    // drivers that have simple _support methods
                    // in a blocking manner
                    resolve();
                };

                if ('_support' in driverObject) {
                    if (driverObject._support && typeof driverObject._support === 'function') {
                        driverObject._support().then(setDriverSupport, reject);
                    } else {
                        setDriverSupport(!!driverObject._support);
                    }
                } else {
                    setDriverSupport(true);
                }
            } catch (e) {
                reject(e);
            }
        });

        executeTwoCallbacks(promise, callback, errorCallback);
        return promise;
    };

    LocalForage.prototype.driver = function driver() {
        return this._driver || null;
    };

    LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {
        var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error('Driver not found.'));

        executeTwoCallbacks(getDriverPromise, callback, errorCallback);
        return getDriverPromise;
    };

    LocalForage.prototype.getSerializer = function getSerializer(callback) {
        var serializerPromise = Promise$1.resolve(localforageSerializer);
        executeTwoCallbacks(serializerPromise, callback);
        return serializerPromise;
    };

    LocalForage.prototype.ready = function ready(callback) {
        var self = this;

        var promise = self._driverSet.then(function () {
            if (self._ready === null) {
                self._ready = self._initDriver();
            }

            return self._ready;
        });

        executeTwoCallbacks(promise, callback, callback);
        return promise;
    };

    LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {
        var self = this;

        if (!isArray(drivers)) {
            drivers = [drivers];
        }

        var supportedDrivers = this._getSupportedDrivers(drivers);

        function setDriverToConfig() {
            self._config.driver = self.driver();
        }

        function extendSelfWithDriver(driver) {
            self._extend(driver);
            setDriverToConfig();

            self._ready = self._initStorage(self._config);
            return self._ready;
        }

        function initDriver(supportedDrivers) {
            return function () {
                var currentDriverIndex = 0;

                function driverPromiseLoop() {
                    while (currentDriverIndex < supportedDrivers.length) {
                        var driverName = supportedDrivers[currentDriverIndex];
                        currentDriverIndex++;

                        self._dbInfo = null;
                        self._ready = null;

                        return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop);
                    }

                    setDriverToConfig();
                    var error = new Error('No available storage method found.');
                    self._driverSet = Promise$1.reject(error);
                    return self._driverSet;
                }

                return driverPromiseLoop();
            };
        }

        // There might be a driver initialization in progress
        // so wait for it to finish in order to avoid a possible
        // race condition to set _dbInfo
        var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function () {
            return Promise$1.resolve();
        }) : Promise$1.resolve();

        this._driverSet = oldDriverSetDone.then(function () {
            var driverName = supportedDrivers[0];
            self._dbInfo = null;
            self._ready = null;

            return self.getDriver(driverName).then(function (driver) {
                self._driver = driver._driver;
                setDriverToConfig();
                self._wrapLibraryMethodsWithReady();
                self._initDriver = initDriver(supportedDrivers);
            });
        })["catch"](function () {
            setDriverToConfig();
            var error = new Error('No available storage method found.');
            self._driverSet = Promise$1.reject(error);
            return self._driverSet;
        });

        executeTwoCallbacks(this._driverSet, callback, errorCallback);
        return this._driverSet;
    };

    LocalForage.prototype.supports = function supports(driverName) {
        return !!DriverSupport[driverName];
    };

    LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {
        extend(this, libraryMethodsAndProperties);
    };

    LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {
        var supportedDrivers = [];
        for (var i = 0, len = drivers.length; i < len; i++) {
            var driverName = drivers[i];
            if (this.supports(driverName)) {
                supportedDrivers.push(driverName);
            }
        }
        return supportedDrivers;
    };

    LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {
        // Add a stub for each driver API method that delays the call to the
        // corresponding driver method until localForage is ready. These stubs
        // will be replaced by the driver methods as soon as the driver is
        // loaded, so there is no performance impact.
        for (var i = 0, len = LibraryMethods.length; i < len; i++) {
            callWhenReady(this, LibraryMethods[i]);
        }
    };

    LocalForage.prototype.createInstance = function createInstance(options) {
        return new LocalForage(options);
    };

    return LocalForage;
}();

// The actual localForage object that we expose as a module or via a
// global. It's extended by pulling in one of our other libraries.


var localforage_js = new LocalForage();

module.exports = localforage_js;

},{"3":3}]},{},[4])(4)
});
;
/*
 Copyright 2011-2013 Abdulla Abdurakhmanov
 Original sources are available at https://code.google.com/p/x2js/

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

function X2JS(config) {
    'use strict';

    var VERSION = "1.1.6";

    config = config || {};
    initConfigDefaults();
    initRequiredPolyfills();

    function initConfigDefaults() {
        if (config.escapeMode === undefined) {
            config.escapeMode = true;
        }
        config.attributePrefix = config.attributePrefix || "_";
        config.arrayAccessForm = config.arrayAccessForm || "none";
        config.emptyNodeForm = config.emptyNodeForm || "text";
        if (config.enableToStringFunc === undefined) {
            config.enableToStringFunc = true;
        }
        config.arrayAccessFormPaths = config.arrayAccessFormPaths || [];
        if (config.skipEmptyTextNodesForObj === undefined) {
            config.skipEmptyTextNodesForObj = true;
        }
        if (config.stripWhitespaces === undefined) {
            config.stripWhitespaces = true;
        }
        config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || [];
    }

    var DOMNodeTypes = {
        ELEMENT_NODE: 1,
        TEXT_NODE: 3,
        CDATA_SECTION_NODE: 4,
        COMMENT_NODE: 8,
        DOCUMENT_NODE: 9
    };

    function initRequiredPolyfills() {
        function pad(number) {
            var r = String(number);
            if (r.length === 1) {
                r = '0' + r;
            }
            return r;
        }
        // Hello IE8-
        if (typeof String.prototype.trim !== 'function') {
            String.prototype.trim = function () {
                return this.replace(/^\s+|^\n+|(\s|\n)+$/g, '');
            }
        }
        if (typeof Date.prototype.toISOString !== 'function') {
            // Implementation from http://stackoverflow.com/questions/2573521/how-do-i-output-an-iso-8601-formatted-string-in-javascript
            Date.prototype.toISOString = function () {
                return this.getUTCFullYear()
                  + '-' + pad(this.getUTCMonth() + 1)
                  + '-' + pad(this.getUTCDate())
                  + 'T' + pad(this.getUTCHours())
                  + ':' + pad(this.getUTCMinutes())
                  + ':' + pad(this.getUTCSeconds())
                  + '.' + String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
                  + 'Z';
            };
        }
    }

    function getNodeLocalName(node) {
        var nodeLocalName = node.localName;
        if (nodeLocalName == null) // Yeah, this is IE!! 
            nodeLocalName = node.baseName;
        if (nodeLocalName == null || nodeLocalName == "") // =="" is IE too
            nodeLocalName = node.nodeName;
        return nodeLocalName;
    }

    function getNodePrefix(node) {
        return node.prefix;
    }

    function escapeXmlChars(str) {
        if (typeof (str) == "string")
            return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;');
        else
            return str;
    }

    function unescapeXmlChars(str) {
        return str.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#x27;/g, "'");
    }

    function toArrayAccessForm(obj, childName, path) {
        switch (config.arrayAccessForm) {
            case "property":
                if (!(obj[childName] instanceof Array))
                    obj[childName + "_asArray"] = [obj[childName]];
                else
                    obj[childName + "_asArray"] = obj[childName];
                break;
                /*case "none":
                    break;*/
        }

        if (!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) {
            var idx = 0;
            for (; idx < config.arrayAccessFormPaths.length; idx++) {
                var arrayPath = config.arrayAccessFormPaths[idx];
                if (typeof arrayPath === "string") {
                    if (arrayPath == path)
                        break;
                }
                else
                    if (arrayPath instanceof RegExp) {
                        if (arrayPath.test(path))
                            break;
                    }
                    else
                        if (typeof arrayPath === "function") {
                            if (arrayPath(obj, childName, path))
                                break;
                        }
            }
            if (idx != config.arrayAccessFormPaths.length) {
                obj[childName] = [obj[childName]];
            }
        }
    }

    function fromXmlDateTime(prop) {
        // Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object
        // Improved to support full spec and optional parts
        var bits = prop.split(/[-T:+Z]/g);

        var d = new Date(bits[0], bits[1] - 1, bits[2]);
        var secondBits = bits[5].split("\.");
        d.setHours(bits[3], bits[4], secondBits[0]);
        if (secondBits.length > 1)
            d.setMilliseconds(secondBits[1]);

        // Get supplied time zone offset in minutes
        if (bits[6] && bits[7]) {
            var offsetMinutes = bits[6] * 60 + Number(bits[7]);
            var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+';

            // Apply the sign
            offsetMinutes = 0 + (sign == '-' ? -1 * offsetMinutes : offsetMinutes);

            // Apply offset and local timezone
            d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset())
        }
        else
            if (prop.indexOf("Z", prop.length - 1) !== -1) {
                d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()));
            }

        // d is now a local time equivalent to the supplied time
        return d;
    }

    function checkFromXmlDateTimePaths(value, childName, fullPath) {
        if (config.datetimeAccessFormPaths.length > 0) {
            var path = fullPath.split("\.#")[0];
            var idx = 0;
            for (; idx < config.datetimeAccessFormPaths.length; idx++) {
                var dtPath = config.datetimeAccessFormPaths[idx];
                if (typeof dtPath === "string") {
                    if (dtPath == path)
                        break;
                }
                else
                    if (dtPath instanceof RegExp) {
                        if (dtPath.test(path))
                            break;
                    }
                    else
                        if (typeof dtPath === "function") {
                            if (dtPath(obj, childName, path))
                                break;
                        }
            }
            if (idx != config.datetimeAccessFormPaths.length) {
                return fromXmlDateTime(value);
            }
            else
                return value;
        }
        else
            return value;
    }

    function parseDOMChildren(node, path) {
        if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) {
            var result = new Object;
            var nodeChildren = node.childNodes;
            // Alternative for firstElementChild which is not supported in some environments
            for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
                var child = nodeChildren.item(cidx);
                if (child.nodeType == DOMNodeTypes.ELEMENT_NODE) {
                    var childName = getNodeLocalName(child);
                    result[childName] = parseDOMChildren(child, childName);
                }
            }
            return result;
        }
        else
            if (node.nodeType == DOMNodeTypes.ELEMENT_NODE) {
                var result = new Object;
                result.__cnt = 0;

                var nodeChildren = node.childNodes;

                // Children nodes
                for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
                    var child = nodeChildren.item(cidx); // nodeChildren[cidx];
                    var childName = getNodeLocalName(child);

                    if (child.nodeType != DOMNodeTypes.COMMENT_NODE) {
                        result.__cnt++;
                        if (result[childName] == null) {
                            result[childName] = parseDOMChildren(child, path + "." + childName);
                            toArrayAccessForm(result, childName, path + "." + childName);
                        }
                        else {
                            if (result[childName] != null) {
                                if (!(result[childName] instanceof Array)) {
                                    result[childName] = [result[childName]];
                                    toArrayAccessForm(result, childName, path + "." + childName);
                                }
                            }
                            (result[childName])[result[childName].length] = parseDOMChildren(child, path + "." + childName);
                        }
                    }
                }

                // Attributes
                for (var aidx = 0; aidx < node.attributes.length; aidx++) {
                    var attr = node.attributes.item(aidx); // [aidx];
                    result.__cnt++;
                    result[config.attributePrefix + attr.name] = attr.value;
                }

                // Node namespace prefix
                var nodePrefix = getNodePrefix(node);
                if (nodePrefix != null && nodePrefix != "") {
                    result.__cnt++;
                    result.__prefix = nodePrefix;
                }

                if (result["#text"] != null) {
                    result.__text = result["#text"];
                    if (result.__text instanceof Array) {
                        result.__text = result.__text.join("\n");
                    }
                    if (config.escapeMode)
                        result.__text = unescapeXmlChars(result.__text);
                    if (config.stripWhitespaces)
                        result.__text = result.__text.trim();
                    delete result["#text"];
                    if (config.arrayAccessForm == "property")
                        delete result["#text_asArray"];
                    result.__text = checkFromXmlDateTimePaths(result.__text, childName, path + "." + childName);
                }
                if (result["#cdata-section"] != null) {
                    result.__cdata = result["#cdata-section"];
                    delete result["#cdata-section"];
                    if (config.arrayAccessForm == "property")
                        delete result["#cdata-section_asArray"];
                }

                if (result.__cnt == 1 && result.__text != null) {
                    result = result.__text;
                }
                else
                    if (result.__cnt == 0 && config.emptyNodeForm == "text") {
                        result = '';
                    }
                    else
                        if (result.__cnt > 1 && result.__text != null && config.skipEmptyTextNodesForObj) {
                            if ((config.stripWhitespaces && result.__text == "") || (result.__text.trim() == "")) {
                                delete result.__text;
                            }
                        }
                delete result.__cnt;

                if (config.enableToStringFunc && (result.__text != null || result.__cdata != null)) {
                    result.toString = function () {
                        return (this.__text != null ? this.__text : '') + (this.__cdata != null ? this.__cdata : '');
                    };
                }

                return result;
            }
            else
                if (node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) {
                    return node.nodeValue;
                }
    }

    function startTag(jsonObj, element, attrList, closed) {
        var resultStr = "<" + ((jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element;
        if (attrList != null) {
            for (var aidx = 0; aidx < attrList.length; aidx++) {
                var attrName = attrList[aidx];
                var attrVal = jsonObj[attrName];
                if (config.escapeMode)
                    attrVal = escapeXmlChars(attrVal);
                resultStr += " " + attrName.substr(config.attributePrefix.length) + "='" + attrVal + "'";
            }
        }
        if (!closed)
            resultStr += ">";
        else
            resultStr += "/>";
        return resultStr;
    }

    function endTag(jsonObj, elementName) {
        return "</" + (jsonObj.__prefix != null ? (jsonObj.__prefix + ":") : "") + elementName + ">";
    }

    function endsWith(str, suffix) {
        return str.indexOf(suffix, str.length - suffix.length) !== -1;
    }

    function jsonXmlSpecialElem(jsonObj, jsonObjField) {
        if ((config.arrayAccessForm == "property" && endsWith(jsonObjField.toString(), ("_asArray")))
				|| jsonObjField.toString().indexOf(config.attributePrefix) == 0
				|| jsonObjField.toString().indexOf("__") == 0
				|| (jsonObj[jsonObjField] instanceof Function))
            return true;
        else
            return false;
    }

    function jsonXmlElemCount(jsonObj) {
        var elementsCnt = 0;
        if (jsonObj instanceof Object) {
            for (var it in jsonObj) {
                if (jsonXmlSpecialElem(jsonObj, it))
                    continue;
                elementsCnt++;
            }
        }
        return elementsCnt;
    }

    function parseJSONAttributes(jsonObj) {
        var attrList = [];
        if (jsonObj instanceof Object) {
            for (var ait in jsonObj) {
                if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf(config.attributePrefix) == 0) {
                    attrList.push(ait);
                }
            }
        }
        return attrList;
    }

    function parseJSONTextAttrs(jsonTxtObj) {
        var result = "";

        if (jsonTxtObj.__cdata != null) {
            result += "<![CDATA[" + jsonTxtObj.__cdata + "]]>";
        }

        if (jsonTxtObj.__text != null) {
            if (config.escapeMode)
                result += escapeXmlChars(jsonTxtObj.__text);
            else
                result += jsonTxtObj.__text;
        }
        return result;
    }

    function parseJSONTextObject(jsonTxtObj) {
        var result = "";

        if (jsonTxtObj instanceof Object) {
            result += parseJSONTextAttrs(jsonTxtObj);
        }
        else
            if (jsonTxtObj != null) {
                if (config.escapeMode)
                    result += escapeXmlChars(jsonTxtObj);
                else
                    result += jsonTxtObj;
            }

        return result;
    }

    function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) {
        var result = "";
        if (jsonArrRoot.length == 0) {
            result += startTag(jsonArrRoot, jsonArrObj, attrList, true);
        }
        else {
            for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
                result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
                result += parseJSONObject(jsonArrRoot[arIdx]);
                result += endTag(jsonArrRoot[arIdx], jsonArrObj);
            }
        }
        return result;
    }

    function parseJSONObject(jsonObj) {
        var result = "";

        var elementsCnt = jsonXmlElemCount(jsonObj);

        if (elementsCnt > 0) {
            for (var it in jsonObj) {

                if (jsonXmlSpecialElem(jsonObj, it))
                    continue;

                var subObj = jsonObj[it];

                var attrList = parseJSONAttributes(subObj)

                if (subObj == null || subObj == undefined) {
                    result += startTag(subObj, it, attrList, true);
                }
                else
                    if (subObj instanceof Object) {

                        if (subObj instanceof Array) {
                            result += parseJSONArray(subObj, it, attrList);
                        }
                        else if (subObj instanceof Date) {
                            result += startTag(subObj, it, attrList, false);
                            result += subObj.toISOString();
                            result += endTag(subObj, it);
                        }
                        else {
                            var subObjElementsCnt = jsonXmlElemCount(subObj);
                            if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) {
                                result += startTag(subObj, it, attrList, false);
                                result += parseJSONObject(subObj);
                                result += endTag(subObj, it);
                            }
                            else {
                                result += startTag(subObj, it, attrList, true);
                            }
                        }
                    }
                    else {
                        result += startTag(subObj, it, attrList, false);
                        result += parseJSONTextObject(subObj);
                        result += endTag(subObj, it);
                    }
            }
        }
        result += parseJSONTextObject(jsonObj);

        return result;
    }

    this.parseXmlString = function (xmlDocStr) {
        var isIEParser = window.ActiveXObject || "ActiveXObject" in window;
        if (xmlDocStr === undefined) {
            return null;
        }
        var xmlDoc;
        if (window.DOMParser) {
            var parser = new window.DOMParser();
            var parsererrorNS = null;
            // IE9+ now is here
            if (!isIEParser) {
                try {
                    parsererrorNS = parser.parseFromString("INVALID", "text/xml").childNodes[0].namespaceURI;
                }
                catch (err) {
                    parsererrorNS = null;
                }
            }
            try {
                xmlDoc = parser.parseFromString(xmlDocStr, "text/xml");
                if (parsererrorNS != null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) {
                    //throw new Error('Error parsing XML: '+xmlDocStr);
                    xmlDoc = null;
                }
            }
            catch (err) {
                xmlDoc = null;
            }
        }
        else {
            // IE :(
            if (xmlDocStr.indexOf("<?") == 0) {
                xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2);
            }
            xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
            xmlDoc.async = "false";
            xmlDoc.loadXML(xmlDocStr);
        }
        return xmlDoc;
    };

    this.asArray = function (prop) {
        if (prop instanceof Array)
            return prop;
        else
            return [prop];
    };

    this.toXmlDateTime = function (dt) {
        if (dt instanceof Date)
            return dt.toISOString();
        else
            if (typeof (dt) === 'number')
                return new Date(dt).toISOString();
            else
                return null;
    };

    this.asDateTime = function (prop) {
        if (typeof (prop) == "string") {
            return fromXmlDateTime(prop);
        }
        else
            return prop;
    };

    this.xml2json = function (xmlDoc) {
        return parseDOMChildren(xmlDoc);
    };

    this.xml_str2json = function (xmlDocStr) {
        var xmlDoc = this.parseXmlString(xmlDocStr);
        if (xmlDoc != null)
            return this.xml2json(xmlDoc);
        else
            return null;
    };

    this.json2xml_str = function (jsonObj) {
        return parseJSONObject(jsonObj);
    };

    this.json2xml = function (jsonObj) {
        var xmlDocStr = this.json2xml_str(jsonObj);
        return this.parseXmlString(xmlDocStr);
    };

    this.getVersion = function () {
        return VERSION;
    };

}
;
/*!
 * jQuery Cookie Plugin v1.4.1
 * https://github.com/carhartl/jquery-cookie
 *
 * Copyright 2013 Klaus Hartl
 * Released under the MIT license
 */
(function (factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // CommonJS
        factory(require('jquery'));
    } else {
        // Browser globals
        factory(jQuery);
    }
}(function ($) {

    var pluses = /\+/g;

    function encode(s) {
        return config.raw ? s : encodeURIComponent(s);
    }

    function decode(s) {
        return config.raw ? s : decodeURIComponent(s);
    }

    function stringifyCookieValue(value) {
        return encode(config.json ? JSON.stringify(value) : String(value));
    }

    function parseCookieValue(s) {
        if (s.indexOf('"') === 0) {
            // This is a quoted cookie as according to RFC2068, unescape...
            s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
        }

        try {
            // Replace server-side written pluses with spaces.
            // If we can't decode the cookie, ignore it, it's unusable.
            // If we can't parse the cookie, ignore it, it's unusable.
            s = decodeURIComponent(s.replace(pluses, ' '));
            return config.json ? JSON.parse(s) : s;
        } catch (e) { }
    }

    function read(s, converter) {
        var value = config.raw ? s : parseCookieValue(s);
        return $.isFunction(converter) ? converter(value) : value;
    }

    var config = $.cookie = function (key, value, options) {

        // Write

        if (value !== undefined && !$.isFunction(value)) {
            options = $.extend({}, config.defaults, options);

            if (typeof options.expires === 'number') {
                var days = options.expires, t = options.expires = new Date();
                t.setTime(+t + days * 864e+5);
            }

            return (document.cookie = [
				encode(key), '=', stringifyCookieValue(value),
				options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
				options.path ? '; path=' + options.path : '',
				options.domain ? '; domain=' + options.domain : '',
				options.secure ? '; secure' : ''
            ].join(''));
        }

        // Read

        var result = key ? undefined : {};

        // To prevent the for loop in the first place assign an empty array
        // in case there are no cookies at all. Also prevents odd result when
        // calling $.cookie().
        var cookies = document.cookie ? document.cookie.split('; ') : [];

        for (var i = 0, l = cookies.length; i < l; i++) {
            var parts = cookies[i].split('=');
            var name = decode(parts.shift());
            var cookie = parts.join('=');

            if (key && key === name) {
                // If second argument (value) is a function it's a converter...
                result = read(cookie, value);
                break;
            }

            // Prevent storing a cookie that we couldn't decode.
            if (!key && (cookie = read(cookie)) !== undefined) {
                result[name] = cookie;
            }
        }

        return result;
    };

    config.defaults = {};

    $.removeCookie = function (key, options) {
        if ($.cookie(key) === undefined) {
            return false;
        }

        // Must not alter options, thus extending a fresh object...
        $.cookie(key, '', $.extend({}, options, { expires: -1 }));
        return !$.cookie(key);
    };

}));;
/*!
 * jQuery BBQ: Back Button & Query Library - v1.3pre - 8/26/2010
 * http://benalman.com/projects/jquery-bbq-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery BBQ: Back Button & Query Library
//
// *Version: 1.3pre, Last updated: 8/26/2010*
// 
// Project Home - http://benalman.com/projects/jquery-bbq-plugin/
// GitHub       - http://github.com/cowboy/jquery-bbq/
// Source       - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.js
// (Minified)   - http://github.com/cowboy/jquery-bbq/raw/master/jquery.ba-bbq.min.js (2.2kb gzipped)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
// 
// Basic AJAX     - http://benalman.com/code/projects/jquery-bbq/examples/fragment-basic/
// Advanced AJAX  - http://benalman.com/code/projects/jquery-bbq/examples/fragment-advanced/
// jQuery UI Tabs - http://benalman.com/code/projects/jquery-bbq/examples/fragment-jquery-ui-tabs/
// Deparam        - http://benalman.com/code/projects/jquery-bbq/examples/deparam/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
// Unit Tests      - http://benalman.com/code/projects/jquery-bbq/unit/
// 
// About: Release History
// 
// 1.3pre - (8/26/2010) Integrated <jQuery hashchange event> v1.3, which adds
//         document.title and document.domain support in IE6/7, BlackBerry
//         support, better Iframe hiding for accessibility reasons, and the new
//         <jQuery.fn.hashchange> "shortcut" method. Added the
//         <jQuery.param.sorted> method which reduces the possibility of
//         extraneous hashchange event triggering. Added the
//         <jQuery.param.fragment.ajaxCrawlable> method which can be used to
//         enable Google "AJAX Crawlable mode."
// 1.2.1 - (2/17/2010) Actually fixed the stale window.location Safari bug from
//         <jQuery hashchange event> in BBQ, which was the main reason for the
//         previous release!
// 1.2   - (2/16/2010) Integrated <jQuery hashchange event> v1.2, which fixes a
//         Safari bug, the event can now be bound before DOM ready, and IE6/7
//         page should no longer scroll when the event is first bound. Also
//         added the <jQuery.param.fragment.noEscape> method, and reworked the
//         <hashchange event (BBQ)> internal "add" method to be compatible with
//         changes made to the jQuery 1.4.2 special events API.
// 1.1.1 - (1/22/2010) Integrated <jQuery hashchange event> v1.1, which fixes an
//         obscure IE8 EmulateIE7 meta tag compatibility mode bug.
// 1.1   - (1/9/2010) Broke out the jQuery BBQ event.special <hashchange event>
//         functionality into a separate plugin for users who want just the
//         basic event & back button support, without all the extra awesomeness
//         that BBQ provides. This plugin will be included as part of jQuery BBQ,
//         but also be available separately. See <jQuery hashchange event>
//         plugin for more information. Also added the <jQuery.bbq.removeState>
//         method and added additional <jQuery.deparam> examples.
// 1.0.3 - (12/2/2009) Fixed an issue in IE 6 where location.search and
//         location.hash would report incorrectly if the hash contained the ?
//         character. Also <jQuery.param.querystring> and <jQuery.param.fragment>
//         will no longer parse params out of a URL that doesn't contain ? or #,
//         respectively.
// 1.0.2 - (10/10/2009) Fixed an issue in IE 6/7 where the hidden IFRAME caused
//         a "This page contains both secure and nonsecure items." warning when
//         used on an https:// page.
// 1.0.1 - (10/7/2009) Fixed an issue in IE 8. Since both "IE7" and "IE8
//         Compatibility View" modes erroneously report that the browser
//         supports the native window.onhashchange event, a slightly more
//         robust test needed to be added.
// 1.0   - (10/2/2009) Initial release

(function($,window){
  '$:nomunge'; // Used by YUI compressor.
  
  // Some convenient shortcuts.
  var undefined,
    aps = Array.prototype.slice,
    decode = decodeURIComponent,
    
    // Method / object references.
    jq_param = $.param,
    jq_param_sorted,
    jq_param_fragment,
    jq_deparam,
    jq_deparam_fragment,
    jq_bbq = $.bbq = $.bbq || {},
    jq_bbq_pushState,
    jq_bbq_getState,
    jq_elemUrlAttr,
    special = $.event.special,
    
    // Reused strings.
    str_hashchange = 'hashchange',
    str_querystring = 'querystring',
    str_fragment = 'fragment',
    str_elemUrlAttr = 'elemUrlAttr',
    str_href = 'href',
    str_src = 'src',
    
    // Reused RegExp.
    re_params_querystring = /^.*\?|#.*$/g,
    re_params_fragment,
    re_fragment,
    re_no_escape,
    
    ajax_crawlable,
    fragment_prefix,
    
    // Used by jQuery.elemUrlAttr.
    elemUrlAttr_cache = {};
  
  // A few commonly used bits, broken out to help reduce minified file size.
  
  function is_string( arg ) {
    return typeof arg === 'string';
  };
  
  // Why write the same function twice? Let's curry! Mmmm, curry..
  
  function curry( func ) {
    var args = aps.call( arguments, 1 );
    
    return function() {
      return func.apply( this, args.concat( aps.call( arguments ) ) );
    };
  };
  
  // Get location.hash (or what you'd expect location.hash to be) sans any
  // leading #. Thanks for making this necessary, Firefox!
  function get_fragment( url ) {
    return url.replace( re_fragment, '$2' );
  };
  
  // Get location.search (or what you'd expect location.search to be) sans any
  // leading #. Thanks for making this necessary, IE6!
  function get_querystring( url ) {
    return url.replace( /(?:^[^?#]*\?([^#]*).*$)?.*/, '$1' );
  };
  
  // Section: Param (to string)
  // 
  // Method: jQuery.param.querystring
  // 
  // Retrieve the query string from a URL or if no arguments are passed, the
  // current window.location.href.
  // 
  // Usage:
  // 
  // > jQuery.param.querystring( [ url ] );
  // 
  // Arguments:
  // 
  //  url - (String) A URL containing query string params to be parsed. If url
  //    is not passed, the current window.location.href is used.
  // 
  // Returns:
  // 
  //  (String) The parsed query string, with any leading "?" removed.
  //
  
  // Method: jQuery.param.querystring (build url)
  // 
  // Merge a URL, with or without pre-existing query string params, plus any
  // object, params string or URL containing query string params into a new URL.
  // 
  // Usage:
  // 
  // > jQuery.param.querystring( url, params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL for params to be merged into. This URL may
  //    contain a query string and/or fragment (hash).
  //  params - (String) A params string or URL containing query string params to
  //    be merged into url.
  //  params - (Object) A params object to be merged into url.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params in the params argument will override any query string
  //         params in url.
  //    * 1: any query string params in url will override params in the params
  //         argument.
  //    * 2: params argument will completely replace any query string in url.
  // 
  // Returns:
  // 
  //  (String) A URL with a urlencoded query string in the format '?a=b&c=d&e=f'.
  
  // Method: jQuery.param.fragment
  // 
  // Retrieve the fragment (hash) from a URL or if no arguments are passed, the
  // current window.location.href.
  // 
  // Usage:
  // 
  // > jQuery.param.fragment( [ url ] );
  // 
  // Arguments:
  // 
  //  url - (String) A URL containing fragment (hash) params to be parsed. If
  //    url is not passed, the current window.location.href is used.
  // 
  // Returns:
  // 
  //  (String) The parsed fragment (hash) string, with any leading "#" removed.
  
  // Method: jQuery.param.fragment (build url)
  // 
  // Merge a URL, with or without pre-existing fragment (hash) params, plus any
  // object, params string or URL containing fragment (hash) params into a new
  // URL.
  // 
  // Usage:
  // 
  // > jQuery.param.fragment( url, params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  url - (String) A valid URL for params to be merged into. This URL may
  //    contain a query string and/or fragment (hash).
  //  params - (String) A params string or URL containing fragment (hash) params
  //    to be merged into url.
  //  params - (Object) A params object to be merged into url.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  // 
  //    * 0: params in the params argument will override any fragment (hash)
  //         params in url.
  //    * 1: any fragment (hash) params in url will override params in the
  //         params argument.
  //    * 2: params argument will completely replace any query string in url.
  // 
  // Returns:
  // 
  //  (String) A URL with a urlencoded fragment (hash) in the format '#a=b&c=d&e=f'.
  
  function jq_param_sub( is_fragment, get_func, url, params, merge_mode ) {
    var result,
      qs,
      matches,
      url_params,
      hash;
    
    if ( params !== undefined ) {
      // Build URL by merging params into url string.
      
      // matches[1] = url part that precedes params, not including trailing ?/#
      // matches[2] = params, not including leading ?/#
      // matches[3] = if in 'querystring' mode, hash including leading #, otherwise ''
      matches = url.match( is_fragment ? re_fragment : /^([^#?]*)\??([^#]*)(#?.*)/ );
      
      // Get the hash if in 'querystring' mode, and it exists.
      hash = matches[3] || '';
      
      if ( merge_mode === 2 && is_string( params ) ) {
        // If merge_mode is 2 and params is a string, merge the fragment / query
        // string into the URL wholesale, without converting it into an object.
        qs = params.replace( is_fragment ? re_params_fragment : re_params_querystring, '' );
        
      } else {
        // Convert relevant params in url to object.
        url_params = jq_deparam( matches[2] );
        
        params = is_string( params )
          
          // Convert passed params string into object.
          ? jq_deparam[ is_fragment ? str_fragment : str_querystring ]( params )
          
          // Passed params object.
          : params;
        
        qs = merge_mode === 2 ? params                              // passed params replace url params
          : merge_mode === 1  ? $.extend( {}, params, url_params )  // url params override passed params
          : $.extend( {}, url_params, params );                     // passed params override url params
        
        // Convert params object into a sorted params string.
        qs = jq_param_sorted( qs );
        
        // Unescape characters specified via $.param.noEscape. Since only hash-
        // history users have requested this feature, it's only enabled for
        // fragment-related params strings.
        if ( is_fragment ) {
          qs = qs.replace( re_no_escape, decode );
        }
      }
      
      // Build URL from the base url, querystring and hash. In 'querystring'
      // mode, ? is only added if a query string exists. In 'fragment' mode, #
      // is always added.
      result = matches[1] + ( is_fragment ? fragment_prefix : qs || !matches[1] ? '?' : '' ) + qs + hash;
      
    } else {
      // If URL was passed in, parse params from URL string, otherwise parse
      // params from window.location.href.
      result = get_func( url !== undefined ? url : location.href );
    }
    
    return result;
  };
  
  jq_param[ str_querystring ]                  = curry( jq_param_sub, 0, get_querystring );
  jq_param[ str_fragment ] = jq_param_fragment = curry( jq_param_sub, 1, get_fragment );
  
  // Method: jQuery.param.sorted
  // 
  // Returns a params string equivalent to that returned by the internal
  // jQuery.param method, but sorted, which makes it suitable for use as a
  // cache key.
  // 
  // For example, in most browsers jQuery.param({z:1,a:2}) returns "z=1&a=2"
  // and jQuery.param({a:2,z:1}) returns "a=2&z=1". Even though both the
  // objects being serialized and the resulting params strings are equivalent,
  // if these params strings were set into the location.hash fragment
  // sequentially, the hashchange event would be triggered unnecessarily, since
  // the strings are different (even though the data described by them is the
  // same). By sorting the params string, unecessary hashchange event triggering
  // can be avoided.
  // 
  // Usage:
  // 
  // > jQuery.param.sorted( obj [, traditional ] );
  // 
  // Arguments:
  // 
  //  obj - (Object) An object to be serialized.
  //  traditional - (Boolean) Params deep/shallow serialization mode. See the
  //    documentation at http://api.jquery.com/jQuery.param/ for more detail.
  // 
  // Returns:
  // 
  //  (String) A sorted params string.
  
  jq_param.sorted = jq_param_sorted = function( a, traditional ) {
    var arr = [],
      obj = {};
    
    $.each( jq_param( a, traditional ).split( '&' ), function(i,v){
      var key = v.replace( /(?:%5B|=).*$/, '' ),
        key_obj = obj[ key ];
      
      if ( !key_obj ) {
        key_obj = obj[ key ] = [];
        arr.push( key );
      }
      
      key_obj.push( v );
    });
    
    return $.map( arr.sort(), function(v){
      return obj[ v ];
    }).join( '&' );
  };
  
  // Method: jQuery.param.fragment.noEscape
  // 
  // Specify characters that will be left unescaped when fragments are created
  // or merged using <jQuery.param.fragment>, or when the fragment is modified
  // using <jQuery.bbq.pushState>. This option only applies to serialized data
  // object fragments, and not set-as-string fragments. Does not affect the
  // query string. Defaults to ",/" (comma, forward slash).
  // 
  // Note that this is considered a purely aesthetic option, and will help to
  // create URLs that "look pretty" in the address bar or bookmarks, without
  // affecting functionality in any way. That being said, be careful to not
  // unescape characters that are used as delimiters or serve a special
  // purpose, such as the "#?&=+" (octothorpe, question mark, ampersand,
  // equals, plus) characters.
  // 
  // Usage:
  // 
  // > jQuery.param.fragment.noEscape( [ chars ] );
  // 
  // Arguments:
  // 
  //  chars - (String) The characters to not escape in the fragment. If
  //    unspecified, defaults to empty string (escape all characters).
  // 
  // Returns:
  // 
  //  Nothing.
  
  jq_param_fragment.noEscape = function( chars ) {
    chars = chars || '';
    var arr = $.map( chars.split(''), encodeURIComponent );
    re_no_escape = new RegExp( arr.join('|'), 'g' );
  };
  
  // A sensible default. These are the characters people seem to complain about
  // "uglifying up the URL" the most.
  jq_param_fragment.noEscape( ',/' );
  
  // Method: jQuery.param.fragment.ajaxCrawlable
  // 
  // TODO: DESCRIBE
  // 
  // Usage:
  // 
  // > jQuery.param.fragment.ajaxCrawlable( [ state ] );
  // 
  // Arguments:
  // 
  //  state - (Boolean) TODO: DESCRIBE
  // 
  // Returns:
  // 
  //  (Boolean) The current ajaxCrawlable state.
  
  jq_param_fragment.ajaxCrawlable = function( state ) {
    if ( state !== undefined ) {
      if ( state ) {
        re_params_fragment = /^.*(?:#!|#)/;
        re_fragment = /^([^#]*)(?:#!|#)?(.*)$/;
        fragment_prefix = '#!';
      } else {
        re_params_fragment = /^.*#/;
        re_fragment = /^([^#]*)#?(.*)$/;
        fragment_prefix = '#';
      }
      ajax_crawlable = !!state;
    }
    
    return ajax_crawlable;
  };
  
  jq_param_fragment.ajaxCrawlable( 0 );
  
  // Section: Deparam (from string)
  // 
  // Method: jQuery.deparam
  // 
  // Deserialize a params string into an object, optionally coercing numbers,
  // booleans, null and undefined values; this method is the counterpart to the
  // internal jQuery.param method.
  // 
  // Usage:
  // 
  // > jQuery.deparam( params [, coerce ] );
  // 
  // Arguments:
  // 
  //  params - (String) A params string to be parsed.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false if omitted.
  // 
  // Returns:
  // 
  //  (Object) An object representing the deserialized params string.
  
  $.deparam = jq_deparam = function( params, coerce ) {
    var obj = {},
      coerce_types = { 'true': !0, 'false': !1, 'null': null };
    
    // Iterate over all name=value pairs.
    $.each( params.replace( /\+/g, ' ' ).split( '&' ), function(j,v){
      var param = v.split( '=' ),
        key = decode( param[0] ),
        val,
        cur = obj,
        i = 0,
        
        // If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
        // into its component parts.
        keys = key.split( '][' ),
        keys_last = keys.length - 1;
      
      // If the first keys part contains [ and the last ends with ], then []
      // are correctly balanced.
      if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
        // Remove the trailing ] from the last keys part.
        keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
        
        // Split first keys part into two parts on the [ and add them back onto
        // the beginning of the keys array.
        keys = keys.shift().split('[').concat( keys );
        
        keys_last = keys.length - 1;
      } else {
        // Basic 'foo' style key.
        keys_last = 0;
      }
      
      // Are we dealing with a name=value pair, or just a name?
      if ( param.length === 2 ) {
        val = decode( param[1] );
        
        // Coerce values.
        if ( coerce ) {
          val = val && !isNaN(val)            ? +val              // number
            : val === 'undefined'             ? undefined         // undefined
            : coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
            : val;                                                // string
        }
        
        if ( keys_last ) {
          // Complex key, build deep object structure based on a few rules:
          // * The 'cur' pointer starts at the object top-level.
          // * [] = array push (n is set to array length), [n] = array if n is 
          //   numeric, otherwise object.
          // * If at the last keys part, set the value.
          // * For each keys part, if the current level is undefined create an
          //   object or array based on the type of the next keys part.
          // * Move the 'cur' pointer to the next level.
          // * Rinse & repeat.
          for ( ; i <= keys_last; i++ ) {
            key = keys[i] === '' ? cur.length : keys[i];
            cur = cur[key] = i < keys_last
              ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
              : val;
          }
          
        } else {
          // Simple key, even simpler rules, since only scalars and shallow
          // arrays are allowed.
          
          if ( $.isArray( obj[key] ) ) {
            // val is already an array, so push on the next value.
            obj[key].push( val );
            
          } else if ( obj[key] !== undefined ) {
            // val isn't an array, but since a second value has been specified,
            // convert val into an array.
            obj[key] = [ obj[key], val ];
            
          } else {
            // val is a scalar.
            obj[key] = val;
          }
        }
        
      } else if ( key ) {
        // No value was defined, so set something meaningful.
        obj[key] = coerce
          ? undefined
          : '';
      }
    });
    
    return obj;
  };
  
  // Method: jQuery.deparam.querystring
  // 
  // Parse the query string from a URL or the current window.location.href,
  // deserializing it into an object, optionally coercing numbers, booleans,
  // null and undefined values.
  // 
  // Usage:
  // 
  // > jQuery.deparam.querystring( [ url ] [, coerce ] );
  // 
  // Arguments:
  // 
  //  url - (String) An optional params string or URL containing query string
  //    params to be parsed. If url is omitted, the current
  //    window.location.href is used.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false if omitted.
  // 
  // Returns:
  // 
  //  (Object) An object representing the deserialized params string.
  
  // Method: jQuery.deparam.fragment
  // 
  // Parse the fragment (hash) from a URL or the current window.location.href,
  // deserializing it into an object, optionally coercing numbers, booleans,
  // null and undefined values.
  // 
  // Usage:
  // 
  // > jQuery.deparam.fragment( [ url ] [, coerce ] );
  // 
  // Arguments:
  // 
  //  url - (String) An optional params string or URL containing fragment (hash)
  //    params to be parsed. If url is omitted, the current window.location.href
  //    is used.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false if omitted.
  // 
  // Returns:
  // 
  //  (Object) An object representing the deserialized params string.
  
  function jq_deparam_sub( is_fragment, url_or_params, coerce ) {
    if ( url_or_params === undefined || typeof url_or_params === 'boolean' ) {
      // url_or_params not specified.
      coerce = url_or_params;
      url_or_params = jq_param[ is_fragment ? str_fragment : str_querystring ]();
    } else {
      url_or_params = is_string( url_or_params )
        ? url_or_params.replace( is_fragment ? re_params_fragment : re_params_querystring, '' )
        : url_or_params;
    }
    
    return jq_deparam( url_or_params, coerce );
  };
  
  jq_deparam[ str_querystring ]                    = curry( jq_deparam_sub, 0 );
  jq_deparam[ str_fragment ] = jq_deparam_fragment = curry( jq_deparam_sub, 1 );
  
  // Section: Element manipulation
  // 
  // Method: jQuery.elemUrlAttr
  // 
  // Get the internal "Default URL attribute per tag" list, or augment the list
  // with additional tag-attribute pairs, in case the defaults are insufficient.
  // 
  // In the <jQuery.fn.querystring> and <jQuery.fn.fragment> methods, this list
  // is used to determine which attribute contains the URL to be modified, if
  // an "attr" param is not specified.
  // 
  // Default Tag-Attribute List:
  // 
  //  a      - href
  //  base   - href
  //  iframe - src
  //  img    - src
  //  input  - src
  //  form   - action
  //  link   - href
  //  script - src
  // 
  // Usage:
  // 
  // > jQuery.elemUrlAttr( [ tag_attr ] );
  // 
  // Arguments:
  // 
  //  tag_attr - (Object) An object containing a list of tag names and their
  //    associated default attribute names in the format { tag: 'attr', ... } to
  //    be merged into the internal tag-attribute list.
  // 
  // Returns:
  // 
  //  (Object) An object containing all stored tag-attribute values.
  
  // Only define function and set defaults if function doesn't already exist, as
  // the urlInternal plugin will provide this method as well.
  $[ str_elemUrlAttr ] || ($[ str_elemUrlAttr ] = function( obj ) {
    return $.extend( elemUrlAttr_cache, obj );
  })({
    a: str_href,
    base: str_href,
    iframe: str_src,
    img: str_src,
    input: str_src,
    form: 'action',
    link: str_href,
    script: str_src
  });
  
  jq_elemUrlAttr = $[ str_elemUrlAttr ];
  
  // Method: jQuery.fn.querystring
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing query string params) plus any params object or
  // string into a new URL, which is then set into that attribute. Like
  // <jQuery.param.querystring (build url)>, but for all elements in a jQuery
  // collection.
  // 
  // Usage:
  // 
  // > jQuery('selector').querystring( [ attr, ] params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params or url into. See <jQuery.elemUrlAttr> for a list of default
  //    attributes.
  //  params - (Object) A params object to be merged into the URL attribute.
  //  params - (String) A URL containing query string params, or params string
  //    to be merged into the URL attribute.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  //    
  //    * 0: params in the params argument will override any params in attr URL.
  //    * 1: any params in attr URL will override params in the params argument.
  //    * 2: params argument will completely replace any query string in attr
  //         URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  // Method: jQuery.fn.fragment
  // 
  // Update URL attribute in one or more elements, merging the current URL (with
  // or without pre-existing fragment/hash params) plus any params object or
  // string into a new URL, which is then set into that attribute. Like
  // <jQuery.param.fragment (build url)>, but for all elements in a jQuery
  // collection.
  // 
  // Usage:
  // 
  // > jQuery('selector').fragment( [ attr, ] params [, merge_mode ] );
  // 
  // Arguments:
  // 
  //  attr - (String) Optional name of an attribute that will contain a URL to
  //    merge params into. See <jQuery.elemUrlAttr> for a list of default
  //    attributes.
  //  params - (Object) A params object to be merged into the URL attribute.
  //  params - (String) A URL containing fragment (hash) params, or params
  //    string to be merged into the URL attribute.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified, and is as-follows:
  //    
  //    * 0: params in the params argument will override any params in attr URL.
  //    * 1: any params in attr URL will override params in the params argument.
  //    * 2: params argument will completely replace any fragment (hash) in attr
  //         URL.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements, but with modified URL
  //  attribute values.
  
  function jq_fn_sub( mode, force_attr, params, merge_mode ) {
    if ( !is_string( params ) && typeof params !== 'object' ) {
      // force_attr not specified.
      merge_mode = params;
      params = force_attr;
      force_attr = undefined;
    }
    
    return this.each(function(){
      var that = $(this),
        
        // Get attribute specified, or default specified via $.elemUrlAttr.
        attr = force_attr || jq_elemUrlAttr()[ ( this.nodeName || '' ).toLowerCase() ] || '',
        
        // Get URL value.
        url = attr && that.attr( attr ) || '';
      
      // Update attribute with new URL.
      that.attr( attr, jq_param[ mode ]( url, params, merge_mode ) );
    });
    
  };
  
  $.fn[ str_querystring ] = curry( jq_fn_sub, str_querystring );
  $.fn[ str_fragment ]    = curry( jq_fn_sub, str_fragment );
  
  // Section: History, hashchange event
  // 
  // Method: jQuery.bbq.pushState
  // 
  // Adds a 'state' into the browser history at the current position, setting
  // location.hash and triggering any bound <hashchange event> callbacks
  // (provided the new state is different than the previous state).
  // 
  // If no arguments are passed, an empty state is created, which is just a
  // shortcut for jQuery.bbq.pushState( {}, 2 ).
  // 
  // Usage:
  // 
  // > jQuery.bbq.pushState( [ params [, merge_mode ] ] );
  // 
  // Arguments:
  // 
  //  params - (String) A serialized params string or a hash string beginning
  //    with # to merge into location.hash.
  //  params - (Object) A params object to merge into location.hash.
  //  merge_mode - (Number) Merge behavior defaults to 0 if merge_mode is not
  //    specified (unless a hash string beginning with # is specified, in which
  //    case merge behavior defaults to 2), and is as-follows:
  // 
  //    * 0: params in the params argument will override any params in the
  //         current state.
  //    * 1: any params in the current state will override params in the params
  //         argument.
  //    * 2: params argument will completely replace current state.
  // 
  // Returns:
  // 
  //  Nothing.
  // 
  // Additional Notes:
  // 
  //  * Setting an empty state may cause the browser to scroll.
  //  * Unlike the fragment and querystring methods, if a hash string beginning
  //    with # is specified as the params agrument, merge_mode defaults to 2.
  
  jq_bbq.pushState = jq_bbq_pushState = function( params, merge_mode ) {
    if ( is_string( params ) && /^#/.test( params ) && merge_mode === undefined ) {
      // Params string begins with # and merge_mode not specified, so completely
      // overwrite window.location.hash.
      merge_mode = 2;
    }
    
    var has_args = params !== undefined,
      // Merge params into window.location using $.param.fragment.
      url = jq_param_fragment( location.href,
        has_args ? params : {}, has_args ? merge_mode : 2 );
    
    // Set new window.location.href. Note that Safari 3 & Chrome barf on
    // location.hash = '#' so the entire URL is set.
    location.href = url;
  };
  
  // Method: jQuery.bbq.getState
  // 
  // Retrieves the current 'state' from the browser history, parsing
  // location.hash for a specific key or returning an object containing the
  // entire state, optionally coercing numbers, booleans, null and undefined
  // values.
  // 
  // Usage:
  // 
  // > jQuery.bbq.getState( [ key ] [, coerce ] );
  // 
  // Arguments:
  // 
  //  key - (String) An optional state key for which to return a value.
  //  coerce - (Boolean) If true, coerces any numbers or true, false, null, and
  //    undefined to their actual value. Defaults to false.
  // 
  // Returns:
  // 
  //  (Anything) If key is passed, returns the value corresponding with that key
  //    in the location.hash 'state', or undefined. If not, an object
  //    representing the entire 'state' is returned.
  
  jq_bbq.getState = jq_bbq_getState = function( key, coerce ) {
    return key === undefined || typeof key === 'boolean'
      ? jq_deparam_fragment( key ) // 'key' really means 'coerce' here
      : jq_deparam_fragment( coerce )[ key ];
  };
  
  // Method: jQuery.bbq.removeState
  // 
  // Remove one or more keys from the current browser history 'state', creating
  // a new state, setting location.hash and triggering any bound
  // <hashchange event> callbacks (provided the new state is different than
  // the previous state).
  // 
  // If no arguments are passed, an empty state is created, which is just a
  // shortcut for jQuery.bbq.pushState( {}, 2 ).
  // 
  // Usage:
  // 
  // > jQuery.bbq.removeState( [ key [, key ... ] ] );
  // 
  // Arguments:
  // 
  //  key - (String) One or more key values to remove from the current state,
  //    passed as individual arguments.
  //  key - (Array) A single array argument that contains a list of key values
  //    to remove from the current state.
  // 
  // Returns:
  // 
  //  Nothing.
  // 
  // Additional Notes:
  // 
  //  * Setting an empty state may cause the browser to scroll.
  
  jq_bbq.removeState = function( arr ) {
    var state = {};
    
    // If one or more arguments is passed..
    if ( arr !== undefined ) {
      
      // Get the current state.
      state = jq_bbq_getState();
      
      // For each passed key, delete the corresponding property from the current
      // state.
      $.each( $.isArray( arr ) ? arr : arguments, function(i,v){
        delete state[ v ];
      });
    }
    
    // Set the state, completely overriding any existing state.
    jq_bbq_pushState( state, 2 );
  };
  
  // Event: hashchange event (BBQ)
  // 
  // Usage in jQuery 1.4 and newer:
  // 
  // In jQuery 1.4 and newer, the event object passed into any hashchange event
  // callback is augmented with a copy of the location.hash fragment at the time
  // the event was triggered as its event.fragment property. In addition, the
  // event.getState method operates on this property (instead of location.hash)
  // which allows this fragment-as-a-state to be referenced later, even after
  // window.location may have changed.
  // 
  // Note that event.fragment and event.getState are not defined according to
  // W3C (or any other) specification, but will still be available whether or
  // not the hashchange event exists natively in the browser, because of the
  // utility they provide.
  // 
  // The event.fragment property contains the output of <jQuery.param.fragment>
  // and the event.getState method is equivalent to the <jQuery.bbq.getState>
  // method.
  // 
  // > $(window).bind( 'hashchange', function( event ) {
  // >   var hash_str = event.fragment,
  // >     param_obj = event.getState(),
  // >     param_val = event.getState( 'param_name' ),
  // >     param_val_coerced = event.getState( 'param_name', true );
  // >   ...
  // > });
  // 
  // Usage in jQuery 1.3.2:
  // 
  // In jQuery 1.3.2, the event object cannot to be augmented as in jQuery 1.4+,
  // so the fragment state isn't bound to the event object and must instead be
  // parsed using the <jQuery.param.fragment> and <jQuery.bbq.getState> methods.
  // 
  // > $(window).bind( 'hashchange', function( event ) {
  // >   var hash_str = $.param.fragment(),
  // >     param_obj = $.bbq.getState(),
  // >     param_val = $.bbq.getState( 'param_name' ),
  // >     param_val_coerced = $.bbq.getState( 'param_name', true );
  // >   ...
  // > });
  // 
  // Additional Notes:
  // 
  // * Due to changes in the special events API, jQuery BBQ v1.2 or newer is
  //   required to enable the augmented event object in jQuery 1.4.2 and newer.
  // * See <jQuery hashchange event> for more detailed information.
  
  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
    
    // Augmenting the event object with the .fragment property and .getState
    // method requires jQuery 1.4 or newer. Note: with 1.3.2, everything will
    // work, but the event won't be augmented)
    add: function( handleObj ) {
      var old_handler;
      
      function new_handler(e) {
        // e.fragment is set to the value of location.hash (with any leading #
        // removed) at the time the event is triggered.
        var hash = e[ str_fragment ] = jq_param_fragment();
        
        // e.getState() works just like $.bbq.getState(), but uses the
        // e.fragment property stored on the event object.
        e.getState = function( key, coerce ) {
          return key === undefined || typeof key === 'boolean'
            ? jq_deparam( hash, key ) // 'key' really means 'coerce' here
            : jq_deparam( hash, coerce )[ key ];
        };
        
        old_handler.apply( this, arguments );
      };
      
      // This may seem a little complicated, but it normalizes the special event
      // .add method between jQuery 1.4/1.4.1 and 1.4.2+
      if ( $.isFunction( handleObj ) ) {
        // 1.4, 1.4.1
        old_handler = handleObj;
        return new_handler;
      } else {
        // 1.4.2+
        old_handler = handleObj.handler;
        handleObj.handler = new_handler;
      }
    }
    
  });
  
})(jQuery,this);

/*!
 * jQuery hashchange event - v1.3 - 7/21/2010
 * http://benalman.com/projects/jquery-hashchange-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery hashchange event
//
// *Version: 1.3, Last updated: 7/21/2010*
// 
// Project Home - http://benalman.com/projects/jquery-hashchange-plugin/
// GitHub       - http://github.com/cowboy/jquery-hashchange/
// Source       - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.js
// (Minified)   - http://github.com/cowboy/jquery-hashchange/raw/master/jquery.ba-hashchange.min.js (0.8kb gzipped)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// These working examples, complete with fully commented code, illustrate a few
// ways in which this plugin can be used.
// 
// hashchange event - http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/
// document.domain - http://benalman.com/code/projects/jquery-hashchange/examples/document_domain/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.2.6, 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-4, Chrome 5-6, Safari 3.2-5,
//                   Opera 9.6-10.60, iPhone 3.1, Android 1.6-2.2, BlackBerry 4.6-5.
// Unit Tests      - http://benalman.com/code/projects/jquery-hashchange/unit/
// 
// About: Known issues
// 
// While this jQuery hashchange event implementation is quite stable and
// robust, there are a few unfortunate browser bugs surrounding expected
// hashchange event-based behaviors, independent of any JavaScript
// window.onhashchange abstraction. See the following examples for more
// information:
// 
// Chrome: Back Button - http://benalman.com/code/projects/jquery-hashchange/examples/bug-chrome-back-button/
// Firefox: Remote XMLHttpRequest - http://benalman.com/code/projects/jquery-hashchange/examples/bug-firefox-remote-xhr/
// WebKit: Back Button in an Iframe - http://benalman.com/code/projects/jquery-hashchange/examples/bug-webkit-hash-iframe/
// Safari: Back Button from a different domain - http://benalman.com/code/projects/jquery-hashchange/examples/bug-safari-back-from-diff-domain/
// 
// Also note that should a browser natively support the window.onhashchange 
// event, but not report that it does, the fallback polling loop will be used.
// 
// About: Release History
// 
// 1.3   - (7/21/2010) Reorganized IE6/7 Iframe code to make it more
//         "removable" for mobile-only development. Added IE6/7 document.title
//         support. Attempted to make Iframe as hidden as possible by using
//         techniques from http://www.paciellogroup.com/blog/?p=604. Added 
//         support for the "shortcut" format $(window).hashchange( fn ) and
//         $(window).hashchange() like jQuery provides for built-in events.
//         Renamed jQuery.hashchangeDelay to <jQuery.fn.hashchange.delay> and
//         lowered its default value to 50. Added <jQuery.fn.hashchange.domain>
//         and <jQuery.fn.hashchange.src> properties plus document-domain.html
//         file to address access denied issues when setting document.domain in
//         IE6/7.
// 1.2   - (2/11/2010) Fixed a bug where coming back to a page using this plugin
//         from a page on another domain would cause an error in Safari 4. Also,
//         IE6/7 Iframe is now inserted after the body (this actually works),
//         which prevents the page from scrolling when the event is first bound.
//         Event can also now be bound before DOM ready, but it won't be usable
//         before then in IE6/7.
// 1.1   - (1/21/2010) Incorporated document.documentMode test to fix IE8 bug
//         where browser version is incorrectly reported as 8.0, despite
//         inclusion of the X-UA-Compatible IE=EmulateIE7 meta tag.
// 1.0   - (1/9/2010) Initial Release. Broke out the jQuery BBQ event.special
//         window.onhashchange functionality into a separate plugin for users
//         who want just the basic event & back button support, without all the
//         extra awesomeness that BBQ provides. This plugin will be included as
//         part of jQuery BBQ, but also be available separately.

(function($,window,undefined){
  '$:nomunge'; // Used by YUI compressor.
  
  // Reused string.
  var str_hashchange = 'hashchange',
    
    // Method / object references.
    doc = document,
    fake_onhashchange,
    special = $.event.special,
    
    // Does the browser support window.onhashchange? Note that IE8 running in
    // IE7 compatibility mode reports true for 'onhashchange' in window, even
    // though the event isn't supported, so also test document.documentMode.
    doc_mode = doc.documentMode,
    supports_onhashchange = 'on' + str_hashchange in window && ( doc_mode === undefined || doc_mode > 7 );
  
  // Get location.hash (or what you'd expect location.hash to be) sans any
  // leading #. Thanks for making this necessary, Firefox!
  function get_fragment( url ) {
    url = url || location.href;
    return '#' + url.replace( /^[^#]*#?(.*)$/, '$1' );
  };
  
  // Method: jQuery.fn.hashchange
  // 
  // Bind a handler to the window.onhashchange event or trigger all bound
  // window.onhashchange event handlers. This behavior is consistent with
  // jQuery's built-in event handlers.
  // 
  // Usage:
  // 
  // > jQuery(window).hashchange( [ handler ] );
  // 
  // Arguments:
  // 
  //  handler - (Function) Optional handler to be bound to the hashchange
  //    event. This is a "shortcut" for the more verbose form:
  //    jQuery(window).bind( 'hashchange', handler ). If handler is omitted,
  //    all bound window.onhashchange event handlers will be triggered. This
  //    is a shortcut for the more verbose
  //    jQuery(window).trigger( 'hashchange' ). These forms are described in
  //    the <hashchange event> section.
  // 
  // Returns:
  // 
  //  (jQuery) The initial jQuery collection of elements.
  
  // Allow the "shortcut" format $(elem).hashchange( fn ) for binding and
  // $(elem).hashchange() for triggering, like jQuery does for built-in events.
  $.fn[ str_hashchange ] = function( fn ) {
    return fn ? this.bind( str_hashchange, fn ) : this.trigger( str_hashchange );
  };
  
  // Property: jQuery.fn.hashchange.delay
  // 
  // The numeric interval (in milliseconds) at which the <hashchange event>
  // polling loop executes. Defaults to 50.
  
  // Property: jQuery.fn.hashchange.domain
  // 
  // If you're setting document.domain in your JavaScript, and you want hash
  // history to work in IE6/7, not only must this property be set, but you must
  // also set document.domain BEFORE jQuery is loaded into the page. This
  // property is only applicable if you are supporting IE6/7 (or IE8 operating
  // in "IE7 compatibility" mode).
  // 
  // In addition, the <jQuery.fn.hashchange.src> property must be set to the
  // path of the included "document-domain.html" file, which can be renamed or
  // modified if necessary (note that the document.domain specified must be the
  // same in both your main JavaScript as well as in this file).
  // 
  // Usage:
  // 
  // jQuery.fn.hashchange.domain = document.domain;
  
  // Property: jQuery.fn.hashchange.src
  // 
  // If, for some reason, you need to specify an Iframe src file (for example,
  // when setting document.domain as in <jQuery.fn.hashchange.domain>), you can
  // do so using this property. Note that when using this property, history
  // won't be recorded in IE6/7 until the Iframe src file loads. This property
  // is only applicable if you are supporting IE6/7 (or IE8 operating in "IE7
  // compatibility" mode).
  // 
  // Usage:
  // 
  // jQuery.fn.hashchange.src = 'path/to/file.html';
  
  $.fn[ str_hashchange ].delay = 50;
  /*
  $.fn[ str_hashchange ].domain = null;
  $.fn[ str_hashchange ].src = null;
  */
  
  // Event: hashchange event
  // 
  // Fired when location.hash changes. In browsers that support it, the native
  // HTML5 window.onhashchange event is used, otherwise a polling loop is
  // initialized, running every <jQuery.fn.hashchange.delay> milliseconds to
  // see if the hash has changed. In IE6/7 (and IE8 operating in "IE7
  // compatibility" mode), a hidden Iframe is created to allow the back button
  // and hash-based history to work.
  // 
  // Usage as described in <jQuery.fn.hashchange>:
  // 
  // > // Bind an event handler.
  // > jQuery(window).hashchange( function(e) {
  // >   var hash = location.hash;
  // >   ...
  // > });
  // > 
  // > // Manually trigger the event handler.
  // > jQuery(window).hashchange();
  // 
  // A more verbose usage that allows for event namespacing:
  // 
  // > // Bind an event handler.
  // > jQuery(window).bind( 'hashchange', function(e) {
  // >   var hash = location.hash;
  // >   ...
  // > });
  // > 
  // > // Manually trigger the event handler.
  // > jQuery(window).trigger( 'hashchange' );
  // 
  // Additional Notes:
  // 
  // * The polling loop and Iframe are not created until at least one handler
  //   is actually bound to the 'hashchange' event.
  // * If you need the bound handler(s) to execute immediately, in cases where
  //   a location.hash exists on page load, via bookmark or page refresh for
  //   example, use jQuery(window).hashchange() or the more verbose 
  //   jQuery(window).trigger( 'hashchange' ).
  // * The event can be bound before DOM ready, but since it won't be usable
  //   before then in IE6/7 (due to the necessary Iframe), recommended usage is
  //   to bind it inside a DOM ready handler.
  
  // Override existing $.event.special.hashchange methods (allowing this plugin
  // to be defined after jQuery BBQ in BBQ's source code).
  special[ str_hashchange ] = $.extend( special[ str_hashchange ], {
    
    // Called only when the first 'hashchange' event is bound to window.
    setup: function() {
      // If window.onhashchange is supported natively, there's nothing to do..
      if ( supports_onhashchange ) { return false; }
      
      // Otherwise, we need to create our own. And we don't want to call this
      // until the user binds to the event, just in case they never do, since it
      // will create a polling loop and possibly even a hidden Iframe.
      $( fake_onhashchange.start );
    },
    
    // Called only when the last 'hashchange' event is unbound from window.
    teardown: function() {
      // If window.onhashchange is supported natively, there's nothing to do..
      if ( supports_onhashchange ) { return false; }
      
      // Otherwise, we need to stop ours (if possible).
      $( fake_onhashchange.stop );
    }
    
  });
  
  // fake_onhashchange does all the work of triggering the window.onhashchange
  // event for browsers that don't natively support it, including creating a
  // polling loop to watch for hash changes and in IE 6/7 creating a hidden
  // Iframe to enable back and forward.
  fake_onhashchange = (function(){
    var self = {},
      timeout_id,
      
      // Remember the initial hash so it doesn't get triggered immediately.
      last_hash = get_fragment(),
      
      fn_retval = function(val){ return val; },
      history_set = fn_retval,
      history_get = fn_retval;
    
    // Start the polling loop.
    self.start = function() {
      timeout_id || poll();
    };
    
    // Stop the polling loop.
    self.stop = function() {
      timeout_id && clearTimeout( timeout_id );
      timeout_id = undefined;
    };
    
    // This polling loop checks every $.fn.hashchange.delay milliseconds to see
    // if location.hash has changed, and triggers the 'hashchange' event on
    // window when necessary.
    function poll() {
      var hash = get_fragment(),
        history_hash = history_get( last_hash );
      
      if ( hash !== last_hash ) {
        history_set( last_hash = hash, history_hash );
        
        $(window).trigger( str_hashchange );
        
      } else if ( history_hash !== last_hash ) {
        location.href = location.href.replace( /#.*/, '' ) + history_hash;
      }
      
      timeout_id = setTimeout( poll, $.fn[ str_hashchange ].delay );
    };
    
    
    return self;
  })();
  
})(jQuery,this);;
/*!
 * Bootstrap v3.1.1 (http://getbootstrap.com)
 * Copyright 2011-2014 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */
if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('<div class="dropdown-backdrop"/>').insertAfter(a(this)).on("click",b);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown",h),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=" li:not(.divider):visible a",i=f.find("[role=menu]"+h+", [role=listbox]"+h);if(i.length){var j=i.index(i.filter(":focus"));38==b.keyCode&&j>0&&j--,40==b.keyCode&&j<i.length-1&&j++,~j||(j=0),i.eq(j).focus()}}}};var g=a.fn.dropdown;a.fn.dropdown=function(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new f(this)),"string"==typeof b&&d[b].call(c)})},a.fn.dropdown.Constructor=f,a.fn.dropdown.noConflict=function(){return a.fn.dropdown=g,this},a(document).on("click.bs.dropdown.data-api",b).on("click.bs.dropdown.data-api",".dropdown form",function(a){a.stopPropagation()}).on("click.bs.dropdown.data-api",e,f.prototype.toggle).on("keydown.bs.dropdown.data-api",e+", [role=menu], [role=listbox]",f.prototype.keydown)}(jQuery),+function(a){"use strict";var b=function(b,c){this.options=c,this.$element=a(b),this.$backdrop=this.isShown=null,this.options.remote&&this.$element.find(".modal-content").load(this.options.remote,a.proxy(function(){this.$element.trigger("loaded.bs.modal")},this))};b.DEFAULTS={backdrop:!0,keyboard:!0,show:!0},b.prototype.toggle=function(a){return this[this.isShown?"hide":"show"](a)},b.prototype.show=function(b){var c=this,d=a.Event("show.bs.modal",{relatedTarget:b});this.$element.trigger(d),this.isShown||d.isDefaultPrevented()||(this.isShown=!0,this.escape(),this.$element.on("click.dismiss.bs.modal",'[data-dismiss="modal"]',a.proxy(this.hide,this)),this.backdrop(function(){var d=a.support.transition&&c.$element.hasClass("fade");c.$element.parent().length||c.$element.appendTo(document.body),c.$element.show().scrollTop(0),d&&c.$element[0].offsetWidth,c.$element.addClass("in").attr("aria-hidden",!1),c.enforceFocus();var e=a.Event("shown.bs.modal",{relatedTarget:b});d?c.$element.find(".modal-dialog").one(a.support.transition.end,function(){c.$element.focus().trigger(e)}).emulateTransitionEnd(300):c.$element.focus().trigger(e)}))},b.prototype.hide=function(b){b&&b.preventDefault(),b=a.Event("hide.bs.modal"),this.$element.trigger(b),this.isShown&&!b.isDefaultPrevented()&&(this.isShown=!1,this.escape(),a(document).off("focusin.bs.modal"),this.$element.removeClass("in").attr("aria-hidden",!0).off("click.dismiss.bs.modal"),a.support.transition&&this.$element.hasClass("fade")?this.$element.one(a.support.transition.end,a.proxy(this.hideModal,this)).emulateTransitionEnd(300):this.hideModal())},b.prototype.enforceFocus=function(){a(document).off("focusin.bs.modal").on("focusin.bs.modal",a.proxy(function(a){this.$element[0]===a.target||this.$element.has(a.target).length||this.$element.focus()},this))},b.prototype.escape=function(){this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.bs.modal",a.proxy(function(a){27==a.which&&this.hide()},this)):this.isShown||this.$element.off("keyup.dismiss.bs.modal")},b.prototype.hideModal=function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden.bs.modal")})},b.prototype.removeBackdrop=function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},b.prototype.backdrop=function(b){var c=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var d=a.support.transition&&c;if(this.$backdrop=a('<div class="modal-backdrop '+c+'" />').appendTo(document.body),this.$element.on("click.dismiss.bs.modal",a.proxy(function(a){a.target===a.currentTarget&&("static"==this.options.backdrop?this.$element[0].focus.call(this.$element[0]):this.hide.call(this))},this)),d&&this.$backdrop[0].offsetWidth,this.$backdrop.addClass("in"),!b)return;d?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()}else!this.isShown&&this.$backdrop?(this.$backdrop.removeClass("in"),a.support.transition&&this.$element.hasClass("fade")?this.$backdrop.one(a.support.transition.end,b).emulateTransitionEnd(150):b()):b&&b()};var c=a.fn.modal;a.fn.modal=function(c,d){return this.each(function(){var e=a(this),f=e.data("bs.modal"),g=a.extend({},b.DEFAULTS,e.data(),"object"==typeof c&&c);f||e.data("bs.modal",f=new b(this,g)),"string"==typeof c?f[c](d):g.show&&f.show(d)})},a.fn.modal.Constructor=b,a.fn.modal.noConflict=function(){return a.fn.modal=c,this},a(document).on("click.bs.modal.data-api",'[data-toggle="modal"]',function(b){var c=a(this),d=c.attr("href"),e=a(c.attr("data-target")||d&&d.replace(/.*(?=#[^\s]+$)/,"")),f=e.data("bs.modal")?"toggle":a.extend({remote:!/#/.test(d)&&d},e.data(),c.data());c.is("a")&&b.preventDefault(),e.modal(f,this).one("hide",function(){c.is(":visible")&&c.focus()})}),a(document).on("show.bs.modal",".modal",function(){a(document.body).addClass("modal-open")}).on("hidden.bs.modal",".modal",function(){a(document.body).removeClass("modal-open")})}(jQuery),+function(a){"use strict";var b=function(a,b){this.type=this.options=this.enabled=this.timeout=this.hoverState=this.$element=null,this.init("tooltip",a,b)};b.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1},b.prototype.init=function(b,c,d){this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d);for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},b.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},b.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show()},b.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type);return clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide()},b.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){if(this.$element.trigger(b),b.isDefaultPrevented())return;var c=this,d=this.tip();this.setContent(),this.options.animation&&d.addClass("fade");var e="function"==typeof this.options.placement?this.options.placement.call(this,d[0],this.$element[0]):this.options.placement,f=/\s?auto?\s?/i,g=f.test(e);g&&(e=e.replace(f,"")||"top"),d.detach().css({top:0,left:0,display:"block"}).addClass(e),this.options.container?d.appendTo(this.options.container):d.insertAfter(this.$element);var h=this.getPosition(),i=d[0].offsetWidth,j=d[0].offsetHeight;if(g){var k=this.$element.parent(),l=e,m=document.documentElement.scrollTop||document.body.scrollTop,n="body"==this.options.container?window.innerWidth:k.outerWidth(),o="body"==this.options.container?window.innerHeight:k.outerHeight(),p="body"==this.options.container?0:k.offset().left;e="bottom"==e&&h.top+h.height+j-m>o?"top":"top"==e&&h.top-m-j<0?"bottom":"right"==e&&h.right+i>n?"left":"left"==e&&h.left-i<p?"right":e,d.removeClass(l).addClass(e)}var q=this.getCalculatedOffset(e,h,i,j);this.applyPlacement(q,e),this.hoverState=null;var r=function(){c.$element.trigger("shown.bs."+c.type)};a.support.transition&&this.$tip.hasClass("fade")?d.one(a.support.transition.end,r).emulateTransitionEnd(150):r()}},b.prototype.applyPlacement=function(b,c){var d,e=this.tip(),f=e[0].offsetWidth,g=e[0].offsetHeight,h=parseInt(e.css("margin-top"),10),i=parseInt(e.css("margin-left"),10);isNaN(h)&&(h=0),isNaN(i)&&(i=0),b.top=b.top+h,b.left=b.left+i,a.offset.setOffset(e[0],a.extend({using:function(a){e.css({top:Math.round(a.top),left:Math.round(a.left)})}},b),0),e.addClass("in");var j=e[0].offsetWidth,k=e[0].offsetHeight;if("top"==c&&k!=g&&(d=!0,b.top=b.top+g-k),/bottom|top/.test(c)){var l=0;b.left<0&&(l=-2*b.left,b.left=0,e.offset(b),j=e[0].offsetWidth,k=e[0].offsetHeight),this.replaceArrow(l-f+j,j,"left")}else this.replaceArrow(k-g,k,"top");d&&e.offset(b)},b.prototype.replaceArrow=function(a,b,c){this.arrow().css(c,a?50*(1-a/b)+"%":"")},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle();a.find(".tooltip-inner")[this.options.html?"html":"text"](b),a.removeClass("fade in top bottom left right")},b.prototype.hide=function(){function b(){"in"!=c.hoverState&&d.detach(),c.$element.trigger("hidden.bs."+c.type)}var c=this,d=this.tip(),e=a.Event("hide.bs."+this.type);return this.$element.trigger(e),e.isDefaultPrevented()?void 0:(d.removeClass("in"),a.support.transition&&this.$tip.hasClass("fade")?d.one(a.support.transition.end,b).emulateTransitionEnd(150):b(),this.hoverState=null,this)},b.prototype.fixTitle=function(){var a=this.$element;(a.attr("title")||"string"!=typeof a.attr("data-original-title"))&&a.attr("data-original-title",a.attr("title")||"").attr("title","")},b.prototype.hasContent=function(){return this.getTitle()},b.prototype.getPosition=function(){var b=this.$element[0];return a.extend({},"function"==typeof b.getBoundingClientRect?b.getBoundingClientRect():{width:b.offsetWidth,height:b.offsetHeight},this.$element.offset())},b.prototype.getCalculatedOffset=function(a,b,c,d){return"bottom"==a?{top:b.top+b.height,left:b.left+b.width/2-c/2}:"top"==a?{top:b.top-d,left:b.left+b.width/2-c/2}:"left"==a?{top:b.top+b.height/2-d/2,left:b.left-c}:{top:b.top+b.height/2-d/2,left:b.left+b.width}},b.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},b.prototype.tip=function(){return this.$tip=this.$tip||a(this.options.template)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},b.prototype.validate=function(){this.$element[0].parentNode||(this.hide(),this.$element=null,this.options=null)},b.prototype.enable=function(){this.enabled=!0},b.prototype.disable=function(){this.enabled=!1},b.prototype.toggleEnabled=function(){this.enabled=!this.enabled},b.prototype.toggle=function(b){var c=b?a(b.currentTarget)[this.type](this.getDelegateOptions()).data("bs."+this.type):this;c.tip().hasClass("in")?c.leave(c):c.enter(c)},b.prototype.destroy=function(){clearTimeout(this.timeout),this.hide().$element.off("."+this.type).removeData("bs."+this.type)};var c=a.fn.tooltip;a.fn.tooltip=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tooltip"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.tooltip",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.tooltip.Constructor=b,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=c,this}}(jQuery),+function(a){"use strict";var b=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");b.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery);;
/* =========================================================
 * bootstrap-datepicker.js
 * Repo: https://github.com/eternicode/bootstrap-datepicker/
 * Demo: http://eternicode.github.io/bootstrap-datepicker/
 * Docs: http://bootstrap-datepicker.readthedocs.org/
 * Forked from http://www.eyecon.ro/bootstrap-datepicker
 * =========================================================
 * Started by Stefan Petre; improvements by Andrew Rowls + contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================= */

(function($, undefined){

	var $window = $(window);

	function UTCDate(){
		return new Date(Date.UTC.apply(Date, arguments));
	}
	function UTCToday(){
		var today = new Date();
		return UTCDate(today.getFullYear(), today.getMonth(), today.getDate());
	}
	function alias(method){
		return function(){
			return this[method].apply(this, arguments);
		};
	}

	var DateArray = (function(){
		var extras = {
			get: function(i){
				return this.slice(i)[0];
			},
			contains: function(d){
				// Array.indexOf is not cross-browser;
				// $.inArray doesn't work with Dates
				var val = d && d.valueOf();
				for (var i=0, l=this.length; i < l; i++)
					if (this[i].valueOf() === val)
						return i;
				return -1;
			},
			remove: function(i){
				this.splice(i,1);
			},
			replace: function(new_array){
				if (!new_array)
					return;
				if (!$.isArray(new_array))
					new_array = [new_array];
				this.clear();
				this.push.apply(this, new_array);
			},
			clear: function(){
				this.splice(0);
			},
			copy: function(){
				var a = new DateArray();
				a.replace(this);
				return a;
			}
		};

		return function(){
			var a = [];
			a.push.apply(a, arguments);
			$.extend(a, extras);
			return a;
		};
	})();


	// Picker object

	var Datepicker = function(element, options){
		this.dates = new DateArray();
		this.viewDate = UTCToday();
		this.focusDate = null;

		this._process_options(options);

		this.element = $(element);
		this.isInline = false;
		this.isInput = this.element.is('input');
		this.component = this.element.is('.date') ? this.element.find('.add-on, .input-group-addon, .btn') : false;
		this.hasInput = this.component && this.element.find('input').length;
		if (this.component && this.component.length === 0)
			this.component = false;

		this.picker = $(DPGlobal.template);
		this._buildEvents();
		this._attachEvents();

		if (this.isInline){
			this.picker.addClass('datepicker-inline').appendTo(this.element);
		}
		else {
			this.picker.addClass('datepicker-dropdown dropdown-menu');
		}

		if (this.o.rtl){
			this.picker.addClass('datepicker-rtl');
		}

		this.viewMode = this.o.startView;

		if (this.o.calendarWeeks)
			this.picker.find('tfoot th.today')
						.attr('colspan', function(i, val){
							return parseInt(val) + 1;
						});

		this._allow_update = false;

		this.setStartDate(this._o.startDate);
		this.setEndDate(this._o.endDate);
		this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);

		this.fillDow();
		this.fillMonths();

		this._allow_update = true;

		this.update();
		this.showMode();

		if (this.isInline){
			this.show();
		}
	};

	Datepicker.prototype = {
		constructor: Datepicker,

		_process_options: function(opts){
			// Store raw options for reference
			this._o = $.extend({}, this._o, opts);
			// Processed options
			var o = this.o = $.extend({}, this._o);

			// Check if "de-DE" style date is available, if not language should
			// fallback to 2 letter code eg "de"
			var lang = o.language;
			if (!dates[lang]){
				lang = lang.split('-')[0];
				if (!dates[lang])
					lang = defaults.language;
			}
			o.language = lang;

			switch (o.startView){
				case 2:
				case 'decade':
					o.startView = 2;
					break;
				case 1:
				case 'year':
					o.startView = 1;
					break;
				default:
					o.startView = 0;
			}

			switch (o.minViewMode){
				case 1:
				case 'months':
					o.minViewMode = 1;
					break;
				case 2:
				case 'years':
					o.minViewMode = 2;
					break;
				default:
					o.minViewMode = 0;
			}

			o.startView = Math.max(o.startView, o.minViewMode);

			// true, false, or Number > 0
			if (o.multidate !== true){
				o.multidate = Number(o.multidate) || false;
				if (o.multidate !== false)
					o.multidate = Math.max(0, o.multidate);
				else
					o.multidate = 1;
			}
			o.multidateSeparator = String(o.multidateSeparator);

			o.weekStart %= 7;
			o.weekEnd = ((o.weekStart + 6) % 7);

			var format = DPGlobal.parseFormat(o.format);
			if (o.startDate !== -Infinity){
				if (!!o.startDate){
					if (o.startDate instanceof Date)
						o.startDate = this._local_to_utc(this._zero_time(o.startDate));
					else
						o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
				}
				else {
					o.startDate = -Infinity;
				}
			}
			if (o.endDate !== Infinity){
				if (!!o.endDate){
					if (o.endDate instanceof Date)
						o.endDate = this._local_to_utc(this._zero_time(o.endDate));
					else
						o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
				}
				else {
					o.endDate = Infinity;
				}
			}

			o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
			if (!$.isArray(o.daysOfWeekDisabled))
				o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
			o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function(d){
				return parseInt(d, 10);
			});

			var plc = String(o.orientation).toLowerCase().split(/\s+/g),
				_plc = o.orientation.toLowerCase();
			plc = $.grep(plc, function(word){
				return (/^auto|left|right|top|bottom$/).test(word);
			});
			o.orientation = {x: 'auto', y: 'auto'};
			if (!_plc || _plc === 'auto')
				; // no action
			else if (plc.length === 1){
				switch (plc[0]){
					case 'top':
					case 'bottom':
						o.orientation.y = plc[0];
						break;
					case 'left':
					case 'right':
						o.orientation.x = plc[0];
						break;
				}
			}
			else {
				_plc = $.grep(plc, function(word){
					return (/^left|right$/).test(word);
				});
				o.orientation.x = _plc[0] || 'auto';

				_plc = $.grep(plc, function(word){
					return (/^top|bottom$/).test(word);
				});
				o.orientation.y = _plc[0] || 'auto';
			}
		},
		_events: [],
		_secondaryEvents: [],
		_applyEvents: function(evs){
			for (var i=0, el, ch, ev; i < evs.length; i++){
				el = evs[i][0];
				if (evs[i].length === 2){
					ch = undefined;
					ev = evs[i][1];
				}
				else if (evs[i].length === 3){
					ch = evs[i][1];
					ev = evs[i][2];
				}
				el.on(ev, ch);
			}
		},
		_unapplyEvents: function(evs){
			for (var i=0, el, ev, ch; i < evs.length; i++){
				el = evs[i][0];
				if (evs[i].length === 2){
					ch = undefined;
					ev = evs[i][1];
				}
				else if (evs[i].length === 3){
					ch = evs[i][1];
					ev = evs[i][2];
				}
				el.off(ev, ch);
			}
		},
		_buildEvents: function(){
			if (this.isInput){ // single input
				this._events = [
					[this.element, {
						focus: $.proxy(this.show, this),
						keyup: $.proxy(function(e){
							//if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
							//	this.update();
						}, this),
						keydown: $.proxy(this.keydown, this)
					}]
				];
			}
			else if (this.component && this.hasInput){ // component: input + button
				this._events = [
					// For components that are not readonly, allow keyboard nav
					[this.element.find('input'), {
						focus: $.proxy(this.show, this),
						keyup: $.proxy(function(e){
							if ($.inArray(e.keyCode, [27,37,39,38,40,32,13,9]) === -1)
								this.update();
						}, this),
						keydown: $.proxy(this.keydown, this)
					}],
					[this.component, {
						click: $.proxy(this.show, this)
					}]
				];
			}
			else if (this.element.is('div')){  // inline datepicker
				this.isInline = true;
			}
			else {
				this._events = [
					[this.element, {
						click: $.proxy(this.show, this)
					}]
				];
			}
			this._events.push(
				// Component: listen for blur on element descendants
				[this.element, '*', {
					blur: $.proxy(function(e){
						this._focused_from = e.target;
					}, this)
				}],
				// Input: listen for blur on element
				[this.element, {
					blur: $.proxy(function(e){
						this._focused_from = e.target;
					}, this)
				}]
			);

			this._secondaryEvents = [
				[this.picker, {
					click: $.proxy(this.click, this)
				}],
				[$(window), {
					resize: $.proxy(this.place, this)
				}],
				[$(document), {
					'mousedown touchstart': $.proxy(function(e){
						// Clicked outside the datepicker, hide it
						if (!(
							this.element.is(e.target) ||
							this.element.find(e.target).length ||
							this.picker.is(e.target) ||
							this.picker.find(e.target).length
						)){
							this.hide();
						}
					}, this)
				}]
			];
		},
		_attachEvents: function(){
			this._detachEvents();
			this._applyEvents(this._events);
		},
		_detachEvents: function(){
			this._unapplyEvents(this._events);
		},
		_attachSecondaryEvents: function(){
			this._detachSecondaryEvents();
			this._applyEvents(this._secondaryEvents);
		},
		_detachSecondaryEvents: function(){
			this._unapplyEvents(this._secondaryEvents);
		},
		_trigger: function(event, altdate){
			var date = altdate || this.dates.get(-1),
				local_date = this._utc_to_local(date);

			this.element.trigger({
				type: event,
				date: local_date,
				dates: $.map(this.dates, this._utc_to_local),
				format: $.proxy(function(ix, format){
					if (arguments.length === 0){
						ix = this.dates.length - 1;
						format = this.o.format;
					}
					else if (typeof ix === 'string'){
						format = ix;
						ix = this.dates.length - 1;
					}
					format = format || this.o.format;
					var date = this.dates.get(ix);
					return DPGlobal.formatDate(date, format, this.o.language);
				}, this)
			});
		},

		show: function(){
			if (!this.isInline)
				this.picker.appendTo('body');
			this.picker.show();
			this.place();
			this._attachSecondaryEvents();
			this._trigger('show');
		},

		hide: function(){
			if (this.isInline)
				return;
			if (!this.picker.is(':visible'))
				return;
			this.focusDate = null;
			this.picker.hide().detach();
			this._detachSecondaryEvents();
			this.viewMode = this.o.startView;
			this.showMode();

			if (
				this.o.forceParse &&
				(
					this.isInput && this.element.val() ||
					this.hasInput && this.element.find('input').val()
				)
			)
				this.setValue();
			this._trigger('hide');
		},

		remove: function(){
			this.hide();
			this._detachEvents();
			this._detachSecondaryEvents();
			this.picker.remove();
			delete this.element.data().datepicker;
			if (!this.isInput){
				delete this.element.data().date;
			}
		},

		_utc_to_local: function(utc){
			return utc && new Date(utc.getTime() + (utc.getTimezoneOffset()*60000));
		},
		_local_to_utc: function(local){
			return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000));
		},
		_zero_time: function(local){
			return local && new Date(local.getFullYear(), local.getMonth(), local.getDate());
		},
		_zero_utc_time: function(utc){
			return utc && new Date(Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate()));
		},

		getDates: function(){
			return $.map(this.dates, this._utc_to_local);
		},

		getUTCDates: function(){
			return $.map(this.dates, function(d){
				return new Date(d);
			});
		},

		getDate: function(){
			return this._utc_to_local(this.getUTCDate());
		},

		getUTCDate: function(){
			return new Date(this.dates.get(-1));
		},

		setDates: function(){
			var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
			this.update.apply(this, args);
			this._trigger('changeDate');
			this.setValue();
		},

		setUTCDates: function(){
			var args = $.isArray(arguments[0]) ? arguments[0] : arguments;
			this.update.apply(this, $.map(args, this._utc_to_local));
			this._trigger('changeDate');
			this.setValue();
		},

		setDate: alias('setDates'),
		setUTCDate: alias('setUTCDates'),

		setValue: function(){
			var formatted = this.getFormattedDate();
			if (!this.isInput){
				if (this.component){
					this.element.find('input').val(formatted).change();
				}
			}
			else {
				this.element.val(formatted).change();
			}
		},

		getFormattedDate: function(format){
			if (format === undefined)
				format = this.o.format;

			var lang = this.o.language;
			return $.map(this.dates, function(d){
				return DPGlobal.formatDate(d, format, lang);
			}).join(this.o.multidateSeparator);
		},

		setStartDate: function(startDate){
			this._process_options({startDate: startDate});
			this.update();
			this.updateNavArrows();
		},

		setEndDate: function(endDate){
			this._process_options({endDate: endDate});
			this.update();
			this.updateNavArrows();
		},

		setDaysOfWeekDisabled: function(daysOfWeekDisabled){
			this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
			this.update();
			this.updateNavArrows();
		},

		place: function(){
			if (this.isInline)
				return;
			var calendarWidth = this.picker.outerWidth(),
				calendarHeight = this.picker.outerHeight(),
				visualPadding = 10,
				windowWidth = $window.width(),
				windowHeight = $window.height(),
				scrollTop = $window.scrollTop();

			var zIndex = parseInt(this.element.parents().filter(function(){
					return $(this).css('z-index') !== 'auto';
				}).first().css('z-index'))+10;
			var offset = this.component ? this.component.parent().offset() : this.element.offset();
			var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
			var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
			var left = offset.left,
				top = offset.top;

			this.picker.removeClass(
				'datepicker-orient-top datepicker-orient-bottom '+
				'datepicker-orient-right datepicker-orient-left'
			);

			if (this.o.orientation.x !== 'auto'){
				this.picker.addClass('datepicker-orient-' + this.o.orientation.x);
				if (this.o.orientation.x === 'right')
					left -= calendarWidth - width;
			}
			// auto x orientation is best-placement: if it crosses a window
			// edge, fudge it sideways
			else {
				// Default to left
				this.picker.addClass('datepicker-orient-left');
				if (offset.left < 0)
					left -= offset.left - visualPadding;
				else if (offset.left + calendarWidth > windowWidth)
					left = windowWidth - calendarWidth - visualPadding;
			}

			// auto y orientation is best-situation: top or bottom, no fudging,
			// decision based on which shows more of the calendar
			var yorient = this.o.orientation.y,
				top_overflow, bottom_overflow;
			if (yorient === 'auto'){
				top_overflow = -scrollTop + offset.top - calendarHeight;
				bottom_overflow = scrollTop + windowHeight - (offset.top + height + calendarHeight);
				if (Math.max(top_overflow, bottom_overflow) === bottom_overflow)
					yorient = 'top';
				else
					yorient = 'bottom';
			}
			this.picker.addClass('datepicker-orient-' + yorient);
			if (yorient === 'top')
				top += height;
			else
				top -= calendarHeight + parseInt(this.picker.css('padding-top'));

			this.picker.css({
				top: top,
				left: left,
				zIndex: zIndex
			});
		},

		_allow_update: true,
		update: function(){
			if (!this._allow_update)
				return;

			var oldDates = this.dates.copy(),
				dates = [],
				fromArgs = false;
			if (arguments.length){
				$.each(arguments, $.proxy(function(i, date){
					if (date instanceof Date)
						date = this._local_to_utc(date);
					dates.push(date);
				}, this));
				fromArgs = true;
			}
			else {
				dates = this.isInput
						? this.element.val()
						: this.element.data('date') || this.element.find('input').val();
				if (dates && this.o.multidate)
					dates = dates.split(this.o.multidateSeparator);
				else
					dates = [dates];
				delete this.element.data().date;
			}

			dates = $.map(dates, $.proxy(function(date){
				return DPGlobal.parseDate(date, this.o.format, this.o.language);
			}, this));
			dates = $.grep(dates, $.proxy(function(date){
				return (
					date < this.o.startDate ||
					date > this.o.endDate ||
					!date
				);
			}, this), true);
			this.dates.replace(dates);

			if (this.dates.length)
				this.viewDate = new Date(this.dates.get(-1));
			else if (this.viewDate < this.o.startDate)
				this.viewDate = new Date(this.o.startDate);
			else if (this.viewDate > this.o.endDate)
				this.viewDate = new Date(this.o.endDate);

			if (fromArgs){
				// setting date by clicking
				this.setValue();
			}
			else if (dates.length){
				// setting date by typing
				if (String(oldDates) !== String(this.dates))
					this._trigger('changeDate');
			}
			if (!this.dates.length && oldDates.length)
				this._trigger('clearDate');

			this.fill();
		},

		fillDow: function(){
			var dowCnt = this.o.weekStart,
				html = '<tr>';
			if (this.o.calendarWeeks){
				var cell = '<th class="cw">&nbsp;</th>';
				html += cell;
				this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
			}
			while (dowCnt < this.o.weekStart + 7){
				html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
			}
			html += '</tr>';
			this.picker.find('.datepicker-days thead').append(html);
		},

		fillMonths: function(){
			var html = '',
			i = 0;
			while (i < 12){
				html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
			}
			this.picker.find('.datepicker-months td').html(html);
		},

		setRange: function(range){
			if (!range || !range.length)
				delete this.range;
			else
				this.range = $.map(range, function(d){
					return d.valueOf();
				});
			this.fill();
		},

		getClassNames: function(date){
			var cls = [],
				year = this.viewDate.getUTCFullYear(),
				month = this.viewDate.getUTCMonth(),
				today = new Date();
			if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){
				cls.push('old');
			}
			else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){
				cls.push('new');
			}
			if (this.focusDate && date.valueOf() === this.focusDate.valueOf())
				cls.push('focused');
			// Compare internal UTC date with local today, not UTC today
			if (this.o.todayHighlight &&
				date.getUTCFullYear() === today.getFullYear() &&
				date.getUTCMonth() === today.getMonth() &&
				date.getUTCDate() === today.getDate()){
				cls.push('today');
			}
			if (this.dates.contains(date) !== -1)
				cls.push('active');
			if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
				$.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1){
				cls.push('disabled');
			}
			if (this.range){
				if (date > this.range[0] && date < this.range[this.range.length-1]){
					cls.push('range');
				}
				if ($.inArray(date.valueOf(), this.range) !== -1){
					cls.push('selected');
				}
			}
			return cls;
		},

		fill: function(){
			var d = new Date(this.viewDate),
				year = d.getUTCFullYear(),
				month = d.getUTCMonth(),
				startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
				startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
				endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
				endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
				todaytxt = dates[this.o.language].today || dates['en'].today || '',
				cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
				tooltip;
			this.picker.find('.datepicker-days thead th.datepicker-switch')
						.text(dates[this.o.language].months[month]+' '+year);
			this.picker.find('tfoot th.today')
						.text(todaytxt)
						.toggle(this.o.todayBtn !== false);
			this.picker.find('tfoot th.clear')
						.text(cleartxt)
						.toggle(this.o.clearBtn !== false);
			this.updateNavArrows();
			this.fillMonths();
			var prevMonth = UTCDate(year, month-1, 28),
				day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
			prevMonth.setUTCDate(day);
			prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
			var nextMonth = new Date(prevMonth);
			nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
			nextMonth = nextMonth.valueOf();
			var html = [];
			var clsName;
			while (prevMonth.valueOf() < nextMonth){
				if (prevMonth.getUTCDay() === this.o.weekStart){
					html.push('<tr>');
					if (this.o.calendarWeeks){
						// ISO 8601: First week contains first thursday.
						// ISO also states week starts on Monday, but we can be more abstract here.
						var
							// Start of current week: based on weekstart/current date
							ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
							// Thursday of this week
							th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
							// First Thursday of year, year from thursday
							yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
							// Calendar week: ms between thursdays, div ms per day, div 7 days
							calWeek =  (th - yth) / 864e5 / 7 + 1;
						html.push('<td class="cw">'+ calWeek +'</td>');

					}
				}
				clsName = this.getClassNames(prevMonth);
				clsName.push('day');

				if (this.o.beforeShowDay !== $.noop){
					var before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
					if (before === undefined)
						before = {};
					else if (typeof(before) === 'boolean')
						before = {enabled: before};
					else if (typeof(before) === 'string')
						before = {classes: before};
					if (before.enabled === false)
						clsName.push('disabled');
					if (before.classes)
						clsName = clsName.concat(before.classes.split(/\s+/));
					if (before.tooltip)
						tooltip = before.tooltip;
				}

				clsName = $.unique(clsName);
				html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
				if (prevMonth.getUTCDay() === this.o.weekEnd){
					html.push('</tr>');
				}
				prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
			}
			this.picker.find('.datepicker-days tbody').empty().append(html.join(''));

			var months = this.picker.find('.datepicker-months')
						.find('th:eq(1)')
							.text(year)
							.end()
						.find('span').removeClass('active');

			$.each(this.dates, function(i, d){
				if (d.getUTCFullYear() === year)
					months.eq(d.getUTCMonth()).addClass('active');
			});

			if (year < startYear || year > endYear){
				months.addClass('disabled');
			}
			if (year === startYear){
				months.slice(0, startMonth).addClass('disabled');
			}
			if (year === endYear){
				months.slice(endMonth+1).addClass('disabled');
			}

			html = '';
			year = parseInt(year/10, 10) * 10;
			var yearCont = this.picker.find('.datepicker-years')
								.find('th:eq(1)')
									.text(year + '-' + (year + 9))
									.end()
								.find('td');
			year -= 1;
			var years = $.map(this.dates, function(d){
					return d.getUTCFullYear();
				}),
				classes;
			for (var i = -1; i < 11; i++){
				classes = ['year'];
				if (i === -1)
					classes.push('old');
				else if (i === 10)
					classes.push('new');
				if ($.inArray(year, years) !== -1)
					classes.push('active');
				if (year < startYear || year > endYear)
					classes.push('disabled');
				html += '<span class="' + classes.join(' ') + '">'+year+'</span>';
				year += 1;
			}
			yearCont.html(html);
		},

		updateNavArrows: function(){
			if (!this._allow_update)
				return;

			var d = new Date(this.viewDate),
				year = d.getUTCFullYear(),
				month = d.getUTCMonth();
			switch (this.viewMode){
				case 0:
					if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()){
						this.picker.find('.prev').css({visibility: 'hidden'});
					}
					else {
						this.picker.find('.prev').css({visibility: 'visible'});
					}
					if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()){
						this.picker.find('.next').css({visibility: 'hidden'});
					}
					else {
						this.picker.find('.next').css({visibility: 'visible'});
					}
					break;
				case 1:
				case 2:
					if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()){
						this.picker.find('.prev').css({visibility: 'hidden'});
					}
					else {
						this.picker.find('.prev').css({visibility: 'visible'});
					}
					if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()){
						this.picker.find('.next').css({visibility: 'hidden'});
					}
					else {
						this.picker.find('.next').css({visibility: 'visible'});
					}
					break;
			}
		},

		click: function(e){
            e.preventDefault();
            var calendarClickTrigger = false;
            var ieCalendarDPSwitchClickTrigger = false;
            var ieCalendarSpanClickTrigger = false;
            var userAgent = navigator.userAgent.toLowerCase();
            msie = /msie (\d+\.?(?:\d+)?)/i.exec(userAgent);
			var target = $(e.target).closest('span, td, th'),
				year, month, day;
			if (target.length === 1){
				switch (target[0].nodeName.toLowerCase()){
					case 'th':
						switch (target[0].className){
							case 'datepicker-switch':
                                this.showMode(1);
                                ieCalendarDPSwitchClickTrigger = true;
								break;
							case 'prev':
                            case 'next':
								var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className === 'prev' ? -1 : 1);
								switch (this.viewMode){
									case 0:
										this.viewDate = this.moveMonth(this.viewDate, dir);
                                        this._trigger('changeMonth', this.viewDate);
                                        calendarClickTrigger = true;
										break;
									case 1:
									case 2:
										this.viewDate = this.moveYear(this.viewDate, dir);
										if (this.viewMode === 1)
											this._trigger('changeYear', this.viewDate);
										break;
								}
								this.fill();
								break;
                           case 'today':
								var date = new Date();
								date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);

								this.showMode(-2);
								var which = this.o.todayBtn === 'linked' ? null : 'view';
								this._setDate(date, which);
								break;
                           case 'clear':
								var element;
								if (this.isInput)
									element = this.element;
								else if (this.component)
									element = this.element.find('input');
								if (element)
									element.val("").change();
								this.update();
								this._trigger('changeDate');
								if (this.o.autoclose)
									this.hide();
								break;
						}
						break;
                    case 'span':
                        ieCalendarSpanClickTrigger = true;
						if (!target.is('.disabled')){
							this.viewDate.setUTCDate(1);
							if (target.is('.month')){
								day = 1;
								month = target.parent().find('span').index(target);
								year = this.viewDate.getUTCFullYear();
								this.viewDate.setUTCMonth(month);
								this._trigger('changeMonth', this.viewDate);
								if (this.o.minViewMode === 1){
									this._setDate(UTCDate(year, month, day));
								}
							}
							else {
								day = 1;
								month = 0;
								year = parseInt(target.text(), 10)||0;
								this.viewDate.setUTCFullYear(year);
								this._trigger('changeYear', this.viewDate);
								if (this.o.minViewMode === 2){
									this._setDate(UTCDate(year, month, day));
								}
							}
							this.showMode(-1);
							this.fill();
						}
						break;
                   case 'td':
						if (target.is('.day') && !target.is('.disabled')){
							day = parseInt(target.text(), 10)||1;
							year = this.viewDate.getUTCFullYear();
							month = this.viewDate.getUTCMonth();
							if (target.is('.old')){
								if (month === 0){
									month = 11;
									year -= 1;
								}
								else {
									month -= 1;
								}
							}
							else if (target.is('.new')){
								if (month === 11){
									month = 0;
									year += 1;
								}
								else {
									month += 1;
								}
							}
							this._setDate(UTCDate(year, month, day));
						}
						break;
				}
            }
            if ((msie && msie.length > 0) || (/trident/i.test(userAgent)) && (ieCalendarDPSwitchClickTrigger || ieCalendarSpanClickTrigger)) {
                (document.getElementsByClassName('datepicker datepicker-dropdown')[0]).focus();
            }
            if (this.picker.is(':visible') && this._focused_from && !calendarClickTrigger) {
                if (this.viewMode === this.o.minViewMode) {
			        $(this._focused_from).focus();
			    }
            }
			delete this._focused_from;
		},

       _toggle_multidate: function(date){
			var ix = this.dates.contains(date);
			if (!date){
				this.dates.clear();
			}
			else if (ix !== -1){
				this.dates.remove(ix);
			}
			else {
				this.dates.push(date);
			}
			if (typeof this.o.multidate === 'number')
				while (this.dates.length > this.o.multidate)
					this.dates.remove(0);
        },
       _setDate: function(date, which){
			if (!which || which === 'date')
				this._toggle_multidate(date && new Date(date));
			if (!which || which  === 'view')
				this.viewDate = date && new Date(date);

			this.fill();
			this.setValue();
			this._trigger('changeDate');
			var element;
			if (this.isInput){
				element = this.element;
			}
			else if (this.component){
				element = this.element.find('input');
			}
			if (element){
				element.change();
			}
			if (this.o.autoclose && (!which || which === 'date')){
				this.hide();
			}
		},

       moveMonth: function(date, dir){
			if (!date)
				return undefined;
			if (!dir)
				return date;
			var new_date = new Date(date.valueOf()),
				day = new_date.getUTCDate(),
				month = new_date.getUTCMonth(),
				mag = Math.abs(dir),
				new_month, test;
			dir = dir > 0 ? 1 : -1;
			if (mag === 1){
				test = dir === -1
					// If going back one month, make sure month is not current month
					// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
					? function(){
						return new_date.getUTCMonth() === month;
					}
					// If going forward one month, make sure month is as expected
					// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
					: function(){
						return new_date.getUTCMonth() !== new_month;
					};
				new_month = month + dir;
				new_date.setUTCMonth(new_month);
				// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
				if (new_month < 0 || new_month > 11)
					new_month = (new_month + 12) % 12;
			}
			else {
				// For magnitudes >1, move one month at a time...
				for (var i=0; i < mag; i++)
					// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
					new_date = this.moveMonth(new_date, dir);
				// ...then reset the day, keeping it in the new month
				new_month = new_date.getUTCMonth();
				new_date.setUTCDate(day);
				test = function(){
					return new_month !== new_date.getUTCMonth();
				};
			}
			// Common date-resetting loop -- if date is beyond end of month, make it
			// end of month
			while (test()){
				new_date.setUTCDate(--day);
				new_date.setUTCMonth(new_month);
			}
			return new_date;
		},

		moveYear: function(date, dir){
			return this.moveMonth(date, dir*12);
		},

		dateWithinRange: function(date){
			return date >= this.o.startDate && date <= this.o.endDate;
		},

		keydown: function(e){
			if (this.picker.is(':not(:visible)')){
				//if (e.keyCode === 27) // allow escape to hide and re-show picker
				//	this.show();
				return;
			}
			var dateChanged = false,
				dir, newDate, newViewDate,
				focusDate = this.focusDate || this.viewDate;
			switch (e.keyCode){
				case 27: // escape
					if (this.focusDate){
						this.focusDate = null;
						this.viewDate = this.dates.get(-1) || this.viewDate;
						this.fill();
					}
					else
						this.hide();
					e.preventDefault();
					break;
				case 37: // left
				case 39: // right
					if (!this.o.keyboardNavigation)
						break;
					dir = e.keyCode === 37 ? -1 : 1;
					if (e.ctrlKey){
						newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
						newViewDate = this.moveYear(focusDate, dir);
						this._trigger('changeYear', this.viewDate);
					}
					else if (e.shiftKey){
						newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
						newViewDate = this.moveMonth(focusDate, dir);
						this._trigger('changeMonth', this.viewDate);
					}
					else {
						newDate = new Date(this.dates.get(-1) || UTCToday());
						newDate.setUTCDate(newDate.getUTCDate() + dir);
						newViewDate = new Date(focusDate);
						newViewDate.setUTCDate(focusDate.getUTCDate() + dir);
					}
					if (this.dateWithinRange(newDate)){
						this.focusDate = this.viewDate = newViewDate;
						this.setValue();
						this.fill();
						e.preventDefault();
					}
					break;
				case 38: // up
				case 40: // down
					if (!this.o.keyboardNavigation)
						break;
					dir = e.keyCode === 38 ? -1 : 1;
					if (e.ctrlKey){
						newDate = this.moveYear(this.dates.get(-1) || UTCToday(), dir);
						newViewDate = this.moveYear(focusDate, dir);
						this._trigger('changeYear', this.viewDate);
					}
					else if (e.shiftKey){
						newDate = this.moveMonth(this.dates.get(-1) || UTCToday(), dir);
						newViewDate = this.moveMonth(focusDate, dir);
						this._trigger('changeMonth', this.viewDate);
					}
					else {
						newDate = new Date(this.dates.get(-1) || UTCToday());
						newDate.setUTCDate(newDate.getUTCDate() + dir * 7);
						newViewDate = new Date(focusDate);
						newViewDate.setUTCDate(focusDate.getUTCDate() + dir * 7);
					}
					if (this.dateWithinRange(newDate)){
						this.focusDate = this.viewDate = newViewDate;
						this.setValue();
						this.fill();
						e.preventDefault();
					}
					break;
				case 32: // spacebar
					// Spacebar is used in manually typing dates in some formats.
					// As such, its behavior should not be hijacked.
					break;
				case 13: // enter
					focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
					this._toggle_multidate(focusDate);
					dateChanged = true;
					this.focusDate = null;
					this.viewDate = this.dates.get(-1) || this.viewDate;
					this.setValue();
					this.fill();
					if (this.picker.is(':visible')){
						e.preventDefault();
						if (this.o.autoclose)
							this.hide();
					}
					break;
				case 9: // tab
					this.focusDate = null;
					this.viewDate = this.dates.get(-1) || this.viewDate;
					this.fill();
					this.hide();
					break;
			}
			if (dateChanged){
				if (this.dates.length)
					this._trigger('changeDate');
				else
					this._trigger('clearDate');
				var element;
				if (this.isInput){
					element = this.element;
				}
				else if (this.component){
					element = this.element.find('input');
				}
				if (element){
					element.change();
				}
			}
		},

		showMode: function(dir){
			if (dir){
				this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
			}
			this.picker
				.find('>div')
				.hide()
				.filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName)
					.css('display', 'block');
			this.updateNavArrows();
		}
	};

	var DateRangePicker = function(element, options){
		this.element = $(element);
		this.inputs = $.map(options.inputs, function(i){
			return i.jquery ? i[0] : i;
		});
		delete options.inputs;

		$(this.inputs)
			.datepicker(options)
			.bind('changeDate', $.proxy(this.dateUpdated, this));

		this.pickers = $.map(this.inputs, function(i){
			return $(i).data('datepicker');
		});
		this.updateDates();
	};
	DateRangePicker.prototype = {
		updateDates: function(){
			this.dates = $.map(this.pickers, function(i){
				return i.getUTCDate();
			});
			this.updateRanges();
		},
		updateRanges: function(){
			var range = $.map(this.dates, function(d){
				return d.valueOf();
			});
			$.each(this.pickers, function(i, p){
				p.setRange(range);
			});
		},
		dateUpdated: function(e){
			// `this.updating` is a workaround for preventing infinite recursion
			// between `changeDate` triggering and `setUTCDate` calling.  Until
			// there is a better mechanism.
			if (this.updating)
				return;
			this.updating = true;

			var dp = $(e.target).data('datepicker'),
				new_date = dp.getUTCDate(),
				i = $.inArray(e.target, this.inputs),
				l = this.inputs.length;
			if (i === -1)
				return;

			$.each(this.pickers, function(i, p){
				if (!p.getUTCDate())
					p.setUTCDate(new_date);
			});

			if (new_date < this.dates[i]){
				// Date being moved earlier/left
				while (i >= 0 && new_date < this.dates[i]){
					this.pickers[i--].setUTCDate(new_date);
				}
			}
			else if (new_date > this.dates[i]){
				// Date being moved later/right
				while (i < l && new_date > this.dates[i]){
					this.pickers[i++].setUTCDate(new_date);
				}
			}
			this.updateDates();

			delete this.updating;
		},
		remove: function(){
			$.map(this.pickers, function(p){ p.remove(); });
			delete this.element.data().datepicker;
		}
	};

	function opts_from_el(el, prefix){
		// Derive options from element data-attrs
		var data = $(el).data(),
			out = {}, inkey,
			replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])');
		prefix = new RegExp('^' + prefix.toLowerCase());
		function re_lower(_,a){
			return a.toLowerCase();
		}
		for (var key in data)
			if (prefix.test(key)){
				inkey = key.replace(replace, re_lower);
				out[inkey] = data[key];
			}
		return out;
	}

	function opts_from_locale(lang){
		// Derive options from locale plugins
		var out = {};
		// Check if "de-DE" style date is available, if not language should
		// fallback to 2 letter code eg "de"
		if (!dates[lang]){
			lang = lang.split('-')[0];
			if (!dates[lang])
				return;
		}
		var d = dates[lang];
		$.each(locale_opts, function(i,k){
			if (k in d)
				out[k] = d[k];
		});
		return out;
	}

	var old = $.fn.datepicker;
	$.fn.datepicker = function(option){
		var args = Array.apply(null, arguments);
		args.shift();
		var internal_return;
		this.each(function(){
			var $this = $(this),
				data = $this.data('datepicker'),
				options = typeof option === 'object' && option;
			if (!data){
				var elopts = opts_from_el(this, 'date'),
					// Preliminary otions
					xopts = $.extend({}, defaults, elopts, options),
					locopts = opts_from_locale(xopts.language),
					// Options priority: js args, data-attrs, locales, defaults
					opts = $.extend({}, defaults, locopts, elopts, options);
				if ($this.is('.input-daterange') || opts.inputs){
					var ropts = {
						inputs: opts.inputs || $this.find('input').toArray()
					};
					$this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
				}
				else {
					$this.data('datepicker', (data = new Datepicker(this, opts)));
				}
			}
			if (typeof option === 'string' && typeof data[option] === 'function'){
				internal_return = data[option].apply(data, args);
				if (internal_return !== undefined)
					return false;
			}
		});
		if (internal_return !== undefined)
			return internal_return;
		else
			return this;
	};

	var defaults = $.fn.datepicker.defaults = {
		autoclose: false,
		beforeShowDay: $.noop,
		calendarWeeks: false,
		clearBtn: false,
		daysOfWeekDisabled: [],
		endDate: Infinity,
		forceParse: true,
		format: 'mm/dd/yyyy',
		keyboardNavigation: true,
		language: 'en',
		minViewMode: 0,
		multidate: false,
		multidateSeparator: ',',
		orientation: "auto",
		rtl: false,
		startDate: -Infinity,
		startView: 0,
		todayBtn: false,
		todayHighlight: false,
		weekStart: 0
	};
	var locale_opts = $.fn.datepicker.locale_opts = [
		'format',
		'rtl',
		'weekStart'
	];
	$.fn.datepicker.Constructor = Datepicker;
	var dates = $.fn.datepicker.dates = {
		en: {
			days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
			daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
			daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
			months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
			monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
			today: "Today",
			clear: "Clear"
		}
	};

	var DPGlobal = {
		modes: [
			{
				clsName: 'days',
				navFnc: 'Month',
				navStep: 1
			},
			{
				clsName: 'months',
				navFnc: 'FullYear',
				navStep: 1
			},
			{
				clsName: 'years',
				navFnc: 'FullYear',
				navStep: 10
		}],
		isLeapYear: function(year){
			return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
		},
		getDaysInMonth: function(year, month){
			return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
		},
		validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
		nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
		parseFormat: function(format){
			// IE treats \0 as a string end in inputs (truncating the value),
			// so it's a bad format delimiter, anyway
			var separators = format.replace(this.validParts, '\0').split('\0'),
				parts = format.match(this.validParts);
			if (!separators || !separators.length || !parts || parts.length === 0){
				throw new Error("Invalid date format.");
			}
			return {separators: separators, parts: parts};
		},
		parseDate: function(date, format, language){
			if (!date)
				return undefined;
			if (date instanceof Date)
				return date;
			if (typeof format === 'string')
				format = DPGlobal.parseFormat(format);
			var part_re = /([\-+]\d+)([dmwy])/,
				parts = date.match(/([\-+]\d+)([dmwy])/g),
				part, dir, i;
			if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)){
				date = new Date();
				for (i=0; i < parts.length; i++){
					part = part_re.exec(parts[i]);
					dir = parseInt(part[1]);
					switch (part[2]){
						case 'd':
							date.setUTCDate(date.getUTCDate() + dir);
							break;
						case 'm':
							date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
							break;
						case 'w':
							date.setUTCDate(date.getUTCDate() + dir * 7);
							break;
						case 'y':
							date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
							break;
					}
				}
				return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
			}
			parts = date && date.match(this.nonpunctuation) || [];
			date = new Date();
			var parsed = {},
				setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
				setters_map = {
					yyyy: function(d,v){
						return d.setUTCFullYear(v);
					},
					yy: function(d,v){
						return d.setUTCFullYear(2000+v);
					},
					m: function(d,v){
						if (isNaN(d))
							return d;
						v -= 1;
						while (v < 0) v += 12;
						v %= 12;
						d.setUTCMonth(v);
						while (d.getUTCMonth() !== v)
							d.setUTCDate(d.getUTCDate()-1);
						return d;
					},
					d: function(d,v){
						return d.setUTCDate(v);
					}
				},
				val, filtered;
			setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
			setters_map['dd'] = setters_map['d'];
			date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
			var fparts = format.parts.slice();
			// Remove noop parts
			if (parts.length !== fparts.length){
				fparts = $(fparts).filter(function(i,p){
					return $.inArray(p, setters_order) !== -1;
				}).toArray();
			}
			// Process remainder
			function match_part(){
				var m = this.slice(0, parts[i].length),
					p = parts[i].slice(0, m.length);
				return m === p;
			}
			if (parts.length === fparts.length){
				var cnt;
				for (i=0, cnt = fparts.length; i < cnt; i++){
					val = parseInt(parts[i], 10);
					part = fparts[i];
					if (isNaN(val)){
						switch (part){
							case 'MM':
								filtered = $(dates[language].months).filter(match_part);
								val = $.inArray(filtered[0], dates[language].months) + 1;
								break;
							case 'M':
								filtered = $(dates[language].monthsShort).filter(match_part);
								val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
								break;
						}
					}
					parsed[part] = val;
				}
				var _date, s;
				for (i=0; i < setters_order.length; i++){
					s = setters_order[i];
					if (s in parsed && !isNaN(parsed[s])){
						_date = new Date(date);
						setters_map[s](_date, parsed[s]);
						if (!isNaN(_date))
							date = _date;
					}
				}
			}
			return date;
		},
		formatDate: function(date, format, language){
			if (!date)
				return '';
			if (typeof format === 'string')
				format = DPGlobal.parseFormat(format);
			var val = {
				d: date.getUTCDate(),
				D: dates[language].daysShort[date.getUTCDay()],
				DD: dates[language].days[date.getUTCDay()],
				m: date.getUTCMonth() + 1,
				M: dates[language].monthsShort[date.getUTCMonth()],
				MM: dates[language].months[date.getUTCMonth()],
				yy: date.getUTCFullYear().toString().substring(2),
				yyyy: date.getUTCFullYear()
			};
			val.dd = (val.d < 10 ? '0' : '') + val.d;
			val.mm = (val.m < 10 ? '0' : '') + val.m;
			date = [];
			var seps = $.extend([], format.separators);
			for (var i=0, cnt = format.parts.length; i <= cnt; i++){
				if (seps.length)
					date.push(seps.shift());
				date.push(val[format.parts[i]]);
			}
			return date.join('');
		},
		headTemplate: '<thead>'+
							'<tr>'+
								'<th class="prev">&laquo;</th>'+
								'<th colspan="5" class="datepicker-switch"></th>'+
								'<th class="next">&raquo;</th>'+
							'</tr>'+
						'</thead>',
		contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
		footTemplate: '<tfoot>'+
							'<tr>'+
								'<th colspan="7" class="today"></th>'+
							'</tr>'+
							'<tr>'+
								'<th colspan="7" class="clear"></th>'+
							'</tr>'+
						'</tfoot>'
	};
	DPGlobal.template = '<div class="datepicker">'+
							'<div class="datepicker-days">'+
								'<table class=" table-condensed">'+
									DPGlobal.headTemplate+
									'<tbody></tbody>'+
									DPGlobal.footTemplate+
								'</table>'+
							'</div>'+
							'<div class="datepicker-months">'+
								'<table class="table-condensed">'+
									DPGlobal.headTemplate+
									DPGlobal.contTemplate+
									DPGlobal.footTemplate+
								'</table>'+
							'</div>'+
							'<div class="datepicker-years">'+
								'<table class="table-condensed">'+
									DPGlobal.headTemplate+
									DPGlobal.contTemplate+
									DPGlobal.footTemplate+
								'</table>'+
							'</div>'+
						'</div>';

	$.fn.datepicker.DPGlobal = DPGlobal;


	/* DATEPICKER NO CONFLICT
	* =================== */

	$.fn.datepicker.noConflict = function(){
		$.fn.datepicker = old;
		return this;
	};


	/* DATEPICKER DATA-API
	* ================== */

	$(document).on(
		'focus.datepicker.data-api click.datepicker.data-api',
		'[data-provide="datepicker"]',
		function(e){
			var $this = $(this);
			if ($this.data('datepicker'))
				return;
			e.preventDefault();
			// component click requires us to explicitly show it
			$this.datepicker('show');
		}
	);
	$(function(){
		$('[data-provide="datepicker-inline"]').datepicker();
	});

}(window.jQuery));
;
/// <reference path="bootstrap2-typeahead.js" />
/* =============================================================
 * bootstrap3-typeahead.js v3.0.3
 * https://github.com/bassjobsen/Bootstrap-3-Typeahead
 * =============================================================
 * Original written by @mdo and @fat
 * =============================================================
 * Copyright 2014 Bass Jobsen @bassjobsen
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================ */


!function ($) {

    "use strict";
    // jshint laxcomma: true


    /* TYPEAHEAD PUBLIC CLASS DEFINITION
     * ================================= */

    var Typeahead = function (element, options) {
        this.$element = $(element);
        this.options = $.extend({}, $.fn.typeahead.defaults, options);
        this.matcher = this.options.matcher || this.matcher;
        this.sorter = this.options.sorter || this.sorter;
        this.select = this.options.select || this.select;
        this.autoSelect = typeof this.options.autoSelect == 'boolean' ? this.options.autoSelect : true;
        this.highlighter = this.options.highlighter || this.highlighter;
        this.updater = this.options.updater || this.updater;
        this.source = this.options.source;
        this.delay = typeof this.options.delay == 'number' ? this.options.delay : 250;
        this.$menu = $(this.options.menu);
        this.shown = false;
        this.listen();
        this.showHintOnFocus = typeof this.options.showHintOnFocus == 'boolean' ? this.options.showHintOnFocus : false;
    };

    Typeahead.prototype = {

        constructor: Typeahead

    , select: function () {
        var val = this.$menu.find('.active').data('value');
        if (this.autoSelect || val) {
            this.$element
              .val(this.updater(val))
              .change();
        }
        return this.hide();
    }

    , updater: function (item) {
        return item;
    }

    , setSource: function (source) {
        this.source = source;
    }

    , show: function () {
        var pos = $.extend({}, this.$element.position(), {
            height: this.$element[0].offsetHeight
        }), scrollHeight;

        scrollHeight = typeof this.options.scrollHeight == 'function' ?
            this.options.scrollHeight.call() :
            this.options.scrollHeight;

        this.$menu
          .insertAfter(this.$element)
          .css({
              top: pos.top + pos.height + scrollHeight
          , left: pos.left
          })
          .show();

        this.shown = true;
        return this;
    }

    , hide: function () {
        this.$menu.hide();
        this.shown = false;
        return this;
    }

    , lookup: function (query) {
        var items;
        if (typeof (query) != 'undefined' && query !== null) {
            this.query = query;
        } else {
            this.query = this.$element.val() || '';
        }

        if (this.query.length < this.options.minLength) {
            return this.shown ? this.hide() : this;
        }

        var worker = $.proxy(function () {
            items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source;
            if (items) {
                this.process(items);
            }
        }, this)

        clearTimeout(this.lookupWorker)
        this.lookupWorker = setTimeout(worker, this.delay)
    }

    , process: function (items) {
        var that = this;

        items = $.grep(items, function (item) {
            return that.matcher(item);
        });

        items = this.sorter(items);

        if (!items.length) {
            return this.shown ? this.hide() : this;
        }

        if (this.options.items == 'all') {
            return this.render(items).show();
        } else {
            return this.render(items.slice(0, this.options.items)).show();
        }
    }

    , matcher: function (item) {
        return ~item.toLowerCase().indexOf(this.query.toLowerCase());
    }

    , sorter: function (items) {
        var beginswith = []
          , caseSensitive = []
          , caseInsensitive = []
          , item;

        while ((item = items.shift())) {
            if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item);
            else if (~item.indexOf(this.query)) caseSensitive.push(item);
            else caseInsensitive.push(item);
        }

        return beginswith.concat(caseSensitive, caseInsensitive);
    }

    , highlighter: function (item) {
        var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
        return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
            return '<strong>' + match + '</strong>';
        });
    }

    , render: function (items) {
        var that = this;

        items = $(items).map(function (i, item) {
            i = $(that.options.item).data('value', item);
            i.find('a').html(that.highlighter(item));
            return i[0];
        });

        if (this.autoSelect) {
            items.first().addClass('active');
        }
        this.$menu.html(items);
        return this;
    }

    , next: function (event) {
        var active = this.$menu.find('.active').removeClass('active')
          , next = active.next();

        if (!next.length) {
            next = $(this.$menu.find('li')[0]);
        }

        next.addClass('active');
    }

    , prev: function (event) {
        var active = this.$menu.find('.active').removeClass('active')
          , prev = active.prev();

        if (!prev.length) {
            prev = this.$menu.find('li').last();
        }

        prev.addClass('active');
    }

    , listen: function () {
        this.$element
          .on('focus', $.proxy(this.focus, this))
          .on('blur', $.proxy(this.blur, this))
          .on('keypress', $.proxy(this.keypress, this))
          .on('keyup', $.proxy(this.keyup, this));

        if (this.eventSupported('keydown')) {
            this.$element.on('keydown', $.proxy(this.keydown, this));
        }

        this.$menu
          .on('click', $.proxy(this.click, this))
          .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
          .on('mouseleave', 'li', $.proxy(this.mouseleave, this));
    }
    , destroy: function () {
        this.$element.data('typeahead', null);
        this.$element
          .off('focus')
          .off('blur')
          .off('keypress')
          .off('keyup');

        if (this.eventSupported('keydown')) {
            this.$element.off('keydown');
        }

        this.$menu.remove();
    }
    , eventSupported: function (eventName) {
        var isSupported = eventName in this.$element;
        if (!isSupported) {
            this.$element.setAttribute(eventName, 'return;');
            isSupported = typeof this.$element[eventName] === 'function';
        }
        return isSupported;
    }

    , move: function (e) {
        if (!this.shown) return;

        switch (e.keyCode) {
            case 9: // tab
            case 13: // enter
            case 27: // escape
                e.preventDefault();
                break;

            case 38: // up arrow
                e.preventDefault();
                this.prev();
                break;

            case 40: // down arrow
                e.preventDefault();
                this.next();
                break;
        }

        e.stopPropagation();
    }

    , keydown: function (e) {
        this.suppressKeyPressRepeat = ~$.inArray(e.keyCode, [40, 38, 9, 13, 27]);
        if (!this.shown && e.keyCode == 40) {
            this.lookup("");
        } else {
            this.move(e);
        }
    }

    , keypress: function (e) {
        if (this.suppressKeyPressRepeat) return;
        this.move(e);
    }

    , keyup: function (e) {
        switch (e.keyCode) {
            case 40: // down arrow
            case 38: // up arrow
            case 16: // shift
            case 17: // ctrl
            case 18: // alt
                break;

            case 9: // tab
            case 13: // enter
                if (!this.shown) return;
                this.select();
                break;

            case 27: // escape
                if (!this.shown) return;
                this.hide();
                break;
            default:
                this.lookup();
        }

        e.stopPropagation();
        e.preventDefault();
    }

    , focus: function (e) {
        if (!this.focused) {
            this.focused = true;
            if (this.options.minLength === 0 && !this.$element.val() || this.options.showHintOnFocus) {
                this.lookup();
            }
        }
    }

    , blur: function (e) {
        this.focused = false;
        if (!this.mousedover && this.shown) this.hide();
    }

    , click: function (e) {
        e.stopPropagation();
        e.preventDefault();
        this.select();
        this.$element.focus();
    }

    , mouseenter: function (e) {
        this.mousedover = true;
        this.$menu.find('.active').removeClass('active');
        $(e.currentTarget).addClass('active');
    }

    , mouseleave: function (e) {
        this.mousedover = false;
        if (!this.focused && this.shown) this.hide();
    }

    };


    /* TYPEAHEAD PLUGIN DEFINITION
     * =========================== */

    var old = $.fn.typeahead;

    $.fn.typeahead = function (option) {
        var arg = arguments;
        return this.each(function () {
            var $this = $(this)
              , data = $this.data('typeahead')
              , options = typeof option == 'object' && option;
            if (!data) $this.data('typeahead', (data = new Typeahead(this, options)));
            if (typeof option == 'string') {
                if (arg.length > 1) {
                    data[option].apply(data, Array.prototype.slice.call(arg, 1));
                } else {
                    data[option]();
                }
            }
        });
    };

    $.fn.typeahead.defaults = {
        source: []
    , items: 8
    , menu: '<ul class="typeahead dropdown-menu"></ul>'
    , item: '<li><a href="#"></a></li>'
    , minLength: 1
    , scrollHeight: 0
    , autoSelect: true
    };

    $.fn.typeahead.Constructor = Typeahead;


    /* TYPEAHEAD NO CONFLICT
     * =================== */

    $.fn.typeahead.noConflict = function () {
        $.fn.typeahead = old;
        return this;
    };


    /* TYPEAHEAD DATA-API
     * ================== */

    $(document).on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
        var $this = $(this);
        if ($this.data('typeahead')) return;
        $this.typeahead($this.data());
    });

}(window.jQuery);;
/*!
 * typeahead.js 0.10.2
 * https://github.com/twitter/typeahead.js
 * Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
 */

(function ($) {
    var _ = {
        isMsie: function () {
            return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false;
        },
        isBlankString: function (str) {
            return !str || /^\s*$/.test(str);
        },
        escapeRegExChars: function (str) {
            return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
        },
        isString: function (obj) {
            return typeof obj === "string";
        },
        isNumber: function (obj) {
            return typeof obj === "number";
        },
        isArray: $.isArray,
        isFunction: $.isFunction,
        isObject: $.isPlainObject,
        isUndefined: function (obj) {
            return typeof obj === "undefined";
        },
        bind: $.proxy,
        each: function (collection, cb) {
            $.each(collection, reverseArgs);
            function reverseArgs(index, value) {
                return cb(value, index);
            }
        },
        map: $.map,
        filter: $.grep,
        every: function (obj, test) {
            var result = true;
            if (!obj) {
                return result;
            }
            $.each(obj, function (key, val) {
                if (!(result = test.call(null, val, key, obj))) {
                    return false;
                }
            });
            return !!result;
        },
        some: function (obj, test) {
            var result = false;
            if (!obj) {
                return result;
            }
            $.each(obj, function (key, val) {
                if (result = test.call(null, val, key, obj)) {
                    return false;
                }
            });
            return !!result;
        },
        mixin: $.extend,
        getUniqueId: function () {
            var counter = 0;
            return function () {
                return counter++;
            };
        }(),
        templatify: function templatify(obj) {
            return $.isFunction(obj) ? obj : template;
            function template() {
                return String(obj);
            }
        },
        defer: function (fn) {
            setTimeout(fn, 0);
        },
        debounce: function (func, wait, immediate) {
            var timeout, result;
            return function () {
                var context = this, args = arguments, later, callNow;
                later = function () {
                    timeout = null;
                    if (!immediate) {
                        result = func.apply(context, args);
                    }
                };
                callNow = immediate && !timeout;
                clearTimeout(timeout);
                timeout = setTimeout(later, wait);
                if (callNow) {
                    result = func.apply(context, args);
                }
                return result;
            };
        },
        throttle: function (func, wait) {
            var context, args, timeout, result, previous, later;
            previous = 0;
            later = function () {
                previous = new Date();
                timeout = null;
                result = func.apply(context, args);
            };
            return function () {
                var now = new Date(), remaining = wait - (now - previous);
                context = this;
                args = arguments;
                if (remaining <= 0) {
                    clearTimeout(timeout);
                    timeout = null;
                    previous = now;
                    result = func.apply(context, args);
                } else if (!timeout) {
                    timeout = setTimeout(later, remaining);
                }
                return result;
            };
        },
        noop: function () { }
    };
    var VERSION = "0.10.2";
    var tokenizers = function (root) {
        return {
            nonword: nonword,
            whitespace: whitespace,
            obj: {
                nonword: getObjTokenizer(nonword),
                whitespace: getObjTokenizer(whitespace)
            }
        };
        function whitespace(s) {
            return s.split(/\s+/);
        }
        function nonword(s) {
            return s.split(/\W+/);
        }
        function getObjTokenizer(tokenizer) {
            return function setKey(key) {
                return function tokenize(o) {
                    return tokenizer(o[key]);
                };
            };
        }
    }();
    var LruCache = function () {
        function LruCache(maxSize) {
            this.maxSize = maxSize || 100;
            this.size = 0;
            this.hash = {};
            this.list = new List();
        }
        _.mixin(LruCache.prototype, {
            set: function set(key, val) {
                var tailItem = this.list.tail, node;
                if (this.size >= this.maxSize) {
                    this.list.remove(tailItem);
                    delete this.hash[tailItem.key];
                }
                if (node = this.hash[key]) {
                    node.val = val;
                    this.list.moveToFront(node);
                } else {
                    node = new Node(key, val);
                    this.list.add(node);
                    this.hash[key] = node;
                    this.size++;
                }
            },
            get: function get(key) {
                var node = this.hash[key];
                if (node) {
                    this.list.moveToFront(node);
                    return node.val;
                }
            }
        });
        function List() {
            this.head = this.tail = null;
        }
        _.mixin(List.prototype, {
            add: function add(node) {
                if (this.head) {
                    node.next = this.head;
                    this.head.prev = node;
                }
                this.head = node;
                this.tail = this.tail || node;
            },
            remove: function remove(node) {
                node.prev ? node.prev.next = node.next : this.head = node.next;
                node.next ? node.next.prev = node.prev : this.tail = node.prev;
            },
            moveToFront: function (node) {
                this.remove(node);
                this.add(node);
            }
        });
        function Node(key, val) {
            this.key = key;
            this.val = val;
            this.prev = this.next = null;
        }
        return LruCache;
    }();
    var PersistentStorage = function () {
        var ls, methods;
        try {
            ls = window.localStorage;
            ls.setItem("~~~", "!");
            ls.removeItem("~~~");
        } catch (err) {
            ls = null;
        }
        function PersistentStorage(namespace) {
            this.prefix = ["__", namespace, "__"].join("");
            this.ttlKey = "__ttl__";
            this.keyMatcher = new RegExp("^" + this.prefix);
        }
        if (ls && window.JSON) {
            methods = {
                _prefix: function (key) {
                    return this.prefix + key;
                },
                _ttlKey: function (key) {
                    return this._prefix(key) + this.ttlKey;
                },
                get: function (key) {
                    if (this.isExpired(key)) {
                        this.remove(key);
                    }
                    return decode(ls.getItem(this._prefix(key)));
                },
                set: function (key, val, ttl) {
                    if (_.isNumber(ttl)) {
                        ls.setItem(this._ttlKey(key), encode(now() + ttl));
                    } else {
                        ls.removeItem(this._ttlKey(key));
                    }
                    return ls.setItem(this._prefix(key), encode(val));
                },
                remove: function (key) {
                    ls.removeItem(this._ttlKey(key));
                    ls.removeItem(this._prefix(key));
                    return this;
                },
                clear: function () {
                    var i, key, keys = [], len = ls.length;
                    for (i = 0; i < len; i++) {
                        if ((key = ls.key(i)).match(this.keyMatcher)) {
                            keys.push(key.replace(this.keyMatcher, ""));
                        }
                    }
                    for (i = keys.length; i--;) {
                        this.remove(keys[i]);
                    }
                    return this;
                },
                isExpired: function (key) {
                    var ttl = decode(ls.getItem(this._ttlKey(key)));
                    return _.isNumber(ttl) && now() > ttl ? true : false;
                }
            };
        } else {
            methods = {
                get: _.noop,
                set: _.noop,
                remove: _.noop,
                clear: _.noop,
                isExpired: _.noop
            };
        }
        _.mixin(PersistentStorage.prototype, methods);
        return PersistentStorage;
        function now() {
            return new Date().getTime();
        }
        function encode(val) {
            return JSON.stringify(_.isUndefined(val) ? null : val);
        }
        function decode(val) {
            return JSON.parse(val);
        }
    }();
    var Transport = function () {
        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, requestCache = new LruCache(10);
        function Transport(o) {
            o = o || {};
            this._send = o.transport ? callbackToDeferred(o.transport) : $.ajax;
            this._get = o.rateLimiter ? o.rateLimiter(this._get) : this._get;
        }
        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
            maxPendingRequests = num;
        };
        Transport.resetCache = function clearCache() {
            requestCache = new LruCache(10);
        };
        _.mixin(Transport.prototype, {
            _get: function (url, o, cb) {
                var that = this, jqXhr;
                if (jqXhr = pendingRequests[url]) {
                    jqXhr.done(done).fail(fail);
                } else if (pendingRequestsCount < maxPendingRequests) {
                    pendingRequestsCount++;
                    pendingRequests[url] = this._send(url, o).done(done).fail(fail).always(always);
                } else {
                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
                }
                function done(resp) {
                    cb && cb(null, resp);
                    requestCache.set(url, resp);
                }
                function fail() {
                    cb && cb(true);
                }
                function always() {
                    pendingRequestsCount--;
                    delete pendingRequests[url];
                    if (that.onDeckRequestArgs) {
                        that._get.apply(that, that.onDeckRequestArgs);
                        that.onDeckRequestArgs = null;
                    }
                }
            },
            get: function (url, o, cb) {
                var resp;
                if (_.isFunction(o)) {
                    cb = o;
                    o = {};
                }
                if (resp = requestCache.get(url)) {
                    _.defer(function () {
                        cb && cb(null, resp);
                    });
                } else {
                    this._get(url, o, cb);
                }
                return !!resp;
            }
        });
        return Transport;
        function callbackToDeferred(fn) {
            return function customSendWrapper(url, o) {
                var deferred = $.Deferred();
                fn(url, o, onSuccess, onError);
                return deferred;
                function onSuccess(resp) {
                    _.defer(function () {
                        deferred.resolve(resp);
                    });
                }
                function onError(err) {
                    _.defer(function () {
                        deferred.reject(err);
                    });
                }
            };
        }
    }();
    var SearchIndex = function () {
        function SearchIndex(o) {
            o = o || {};
            if (!o.datumTokenizer || !o.queryTokenizer) {
                $.error("datumTokenizer and queryTokenizer are both required");
            }
            this.datumTokenizer = o.datumTokenizer;
            this.queryTokenizer = o.queryTokenizer;
            this.reset();
        }
        _.mixin(SearchIndex.prototype, {
            bootstrap: function bootstrap(o) {
                this.datums = o.datums;
                this.trie = o.trie;
            },
            add: function (data) {
                var that = this;
                data = _.isArray(data) ? data : [data];
                _.each(data, function (datum) {
                    var id, tokens;
                    id = that.datums.push(datum) - 1;
                    tokens = normalizeTokens(that.datumTokenizer(datum));
                    _.each(tokens, function (token) {
                        var node, chars, ch;
                        node = that.trie;
                        chars = token.split("");
                        while (ch = chars.shift()) {
                            node = node.children[ch] || (node.children[ch] = newNode());
                            node.ids.push(id);
                        }
                    });
                });
            },
            get: function get(query) {
                var that = this, tokens, matches;
                tokens = normalizeTokens(this.queryTokenizer(query));
                _.each(tokens, function (token) {
                    var node, chars, ch, ids;
                    if (matches && matches.length === 0) {
                        return false;
                    }
                    node = that.trie;
                    chars = token.split("");
                    while (node && (ch = chars.shift())) {
                        node = node.children[ch];
                    }
                    if (node && chars.length === 0) {
                        ids = node.ids.slice(0);
                        matches = matches ? getIntersection(matches, ids) : ids;
                    } else {
                        matches = [];
                        return false;
                    }
                });
                return matches ? _.map(unique(matches), function (id) {
                    return that.datums[id];
                }) : [];
            },
            reset: function reset() {
                this.datums = [];
                this.trie = newNode();
            },
            serialize: function serialize() {
                return {
                    datums: this.datums,
                    trie: this.trie
                };
            }
        });
        return SearchIndex;
        function normalizeTokens(tokens) {
            tokens = _.filter(tokens, function (token) {
                return !!token;
            });
            tokens = _.map(tokens, function (token) {
                return token.toLowerCase();
            });
            return tokens;
        }
        function newNode() {
            return {
                ids: [],
                children: {}
            };
        }
        function unique(array) {
            var seen = {}, uniques = [];
            for (var i = 0; i < array.length; i++) {
                if (!seen[array[i]]) {
                    seen[array[i]] = true;
                    uniques.push(array[i]);
                }
            }
            return uniques;
        }
        function getIntersection(arrayA, arrayB) {
            var ai = 0, bi = 0, intersection = [];
            arrayA = arrayA.sort(compare);
            arrayB = arrayB.sort(compare);
            while (ai < arrayA.length && bi < arrayB.length) {
                if (arrayA[ai] < arrayB[bi]) {
                    ai++;
                } else if (arrayA[ai] > arrayB[bi]) {
                    bi++;
                } else {
                    intersection.push(arrayA[ai]);
                    ai++;
                    bi++;
                }
            }
            return intersection;
            function compare(a, b) {
                return a - b;
            }
        }
    }();
    var oParser = function () {
        return {
            local: getLocal,
            prefetch: getPrefetch,
            remote: getRemote
        };
        function getLocal(o) {
            return o.local || null;
        }
        function getPrefetch(o) {
            var prefetch, defaults;
            defaults = {
                url: null,
                thumbprint: "",
                ttl: 24 * 60 * 60 * 1e3,
                filter: null,
                ajax: {}
            };
            if (prefetch = o.prefetch || null) {
                prefetch = _.isString(prefetch) ? {
                    url: prefetch
                } : prefetch;
                prefetch = _.mixin(defaults, prefetch);
                prefetch.thumbprint = VERSION + prefetch.thumbprint;
                prefetch.ajax.type = prefetch.ajax.type || "GET";
                prefetch.ajax.dataType = prefetch.ajax.dataType || "json";
                !prefetch.url && $.error("prefetch requires url to be set");
            }
            return prefetch;
        }
        function getRemote(o) {
            var remote, defaults;
            defaults = {
                url: null,
                wildcard: "%QUERY",
                replace: null,
                rateLimitBy: "debounce",
                rateLimitWait: 300,
                send: null,
                filter: null,
                ajax: {}
            };
            if (remote = o.remote || null) {
                remote = _.isString(remote) ? {
                    url: remote
                } : remote;
                remote = _.mixin(defaults, remote);
                remote.rateLimiter = /^throttle$/i.test(remote.rateLimitBy) ? byThrottle(remote.rateLimitWait) : byDebounce(remote.rateLimitWait);
                remote.ajax.type = remote.ajax.type || "GET";
                remote.ajax.dataType = remote.ajax.dataType || "json";
                delete remote.rateLimitBy;
                delete remote.rateLimitWait;
                !remote.url && $.error("remote requires url to be set");
            }
            return remote;
            function byDebounce(wait) {
                return function (fn) {
                    return _.debounce(fn, wait);
                };
            }
            function byThrottle(wait) {
                return function (fn) {
                    return _.throttle(fn, wait);
                };
            }
        }
    }();
    (function (root) {
        var old, keys;
        old = root.Bloodhound;
        keys = {
            data: "data",
            protocol: "protocol",
            thumbprint: "thumbprint"
        };
        root.Bloodhound = Bloodhound;
        function Bloodhound(o) {
            if (!o || !o.local && !o.prefetch && !o.remote) {
                $.error("one of local, prefetch, or remote is required");
            }
            this.limit = o.limit || 5;
            this.sorter = getSorter(o.sorter);
            this.dupDetector = o.dupDetector || ignoreDuplicates;
            this.local = oParser.local(o);
            this.prefetch = oParser.prefetch(o);
            this.remote = oParser.remote(o);
            this.cacheKey = this.prefetch ? this.prefetch.cacheKey || this.prefetch.url : null;
            this.index = new SearchIndex({
                datumTokenizer: o.datumTokenizer,
                queryTokenizer: o.queryTokenizer
            });
            this.storage = this.cacheKey ? new PersistentStorage(this.cacheKey) : null;
        }
        Bloodhound.noConflict = function noConflict() {
            root.Bloodhound = old;
            return Bloodhound;
        };
        Bloodhound.tokenizers = tokenizers;
        _.mixin(Bloodhound.prototype, {
            _loadPrefetch: function loadPrefetch(o) {
                var that = this, serialized, deferred;
                if (serialized = this._readFromStorage(o.thumbprint)) {
                    this.index.bootstrap(serialized);
                    deferred = $.Deferred().resolve();
                } else {
                    deferred = $.ajax(o.url, o.ajax).done(handlePrefetchResponse);
                }
                return deferred;
                function handlePrefetchResponse(resp) {
                    that.clear();
                    that.add(o.filter ? o.filter(resp) : resp);
                    that._saveToStorage(that.index.serialize(), o.thumbprint, o.ttl);
                }
            },
            _getFromRemote: function getFromRemote(query, cb) {
                var that = this, url, uriEncodedQuery;
                query = query || "";
                uriEncodedQuery = encodeURIComponent(query);
                url = this.remote.replace ? this.remote.replace(this.remote.url, query) : this.remote.url.replace(this.remote.wildcard, uriEncodedQuery);
                return this.transport.get(url, this.remote.ajax, handleRemoteResponse);
                function handleRemoteResponse(err, resp) {
                    err ? cb([]) : cb(that.remote.filter ? that.remote.filter(resp) : resp);
                }
            },
            _saveToStorage: function saveToStorage(data, thumbprint, ttl) {
                if (this.storage) {
                    this.storage.set(keys.data, data, ttl);
                    this.storage.set(keys.protocol, location.protocol, ttl);
                    this.storage.set(keys.thumbprint, thumbprint, ttl);
                }
            },
            _readFromStorage: function readFromStorage(thumbprint) {
                var stored = {}, isExpired;
                if (this.storage) {
                    stored.data = this.storage.get(keys.data);
                    stored.protocol = this.storage.get(keys.protocol);
                    stored.thumbprint = this.storage.get(keys.thumbprint);
                }
                isExpired = stored.thumbprint !== thumbprint || stored.protocol !== location.protocol;
                return stored.data && !isExpired ? stored.data : null;
            },
            _initialize: function initialize() {
                var that = this, local = this.local, deferred;
                deferred = this.prefetch ? this._loadPrefetch(this.prefetch) : $.Deferred().resolve();
                local && deferred.done(addLocalToIndex);
                this.transport = this.remote ? new Transport(this.remote) : null;
                return this.initPromise = deferred.promise();
                function addLocalToIndex() {
                    that.add(_.isFunction(local) ? local() : local);
                }
            },
            initialize: function initialize(force) {
                return !this.initPromise || force ? this._initialize() : this.initPromise;
            },
            add: function add(data) {
                this.index.add(data);
            },
            get: function get(query, cb) {
                var that = this, matches = [], cacheHit = false;
                matches = this.index.get(query);
                matches = this.sorter(matches).slice(0, this.limit);
                if (matches.length < this.limit && this.transport) {
                    cacheHit = this._getFromRemote(query, returnRemoteMatches);
                }
                if (!cacheHit) {
                    (matches.length > 0 || !this.transport) && cb && cb(matches);
                }
                function returnRemoteMatches(remoteMatches) {
                    var matchesWithBackfill = matches.slice(0);
                    _.each(remoteMatches, function (remoteMatch) {
                        var isDuplicate;
                        isDuplicate = _.some(matchesWithBackfill, function (match) {
                            return that.dupDetector(remoteMatch, match);
                        });
                        !isDuplicate && matchesWithBackfill.push(remoteMatch);
                        return matchesWithBackfill.length < that.limit;
                    });
                    cb && cb(that.sorter(matchesWithBackfill));
                }
            },
            clear: function clear() {
                this.index.reset();
            },
            clearPrefetchCache: function clearPrefetchCache() {
                this.storage && this.storage.clear();
            },
            clearRemoteCache: function clearRemoteCache() {
                this.transport && Transport.resetCache();
            },
            ttAdapter: function ttAdapter() {
                return _.bind(this.get, this);
            }
        });
        return Bloodhound;
        function getSorter(sortFn) {
            return _.isFunction(sortFn) ? sort : noSort;
            function sort(array) {
                return array.sort(sortFn);
            }
            function noSort(array) {
                return array;
            }
        }
        function ignoreDuplicates() {
            return false;
        }
    })(this);
})(window.jQuery);;
/*!
 * Knockout JavaScript library v3.5.1
 * (c) The Knockout.js team - http://knockoutjs.com/
 * License: MIT (http://www.opensource.org/licenses/mit-license.php)
 */

(function(){
var DEBUG=true;
(function(undefined){
    // (0, eval)('this') is a robust way of getting a reference to the global object
    // For details, see http://stackoverflow.com/questions/14119988/return-this-0-evalthis/14120023#14120023
    var window = this || (0, eval)('this'),
        document = window['document'],
        navigator = window['navigator'],
        jQueryInstance = window["jQuery"],
        JSON = window["JSON"];

    if (!jQueryInstance && typeof jQuery !== "undefined") {
        jQueryInstance = jQuery;
    }
(function(factory) {
    // Support three module loading scenarios
    if (typeof define === 'function' && define['amd']) {
        // [1] AMD anonymous module
        define(['exports', 'require'], factory);
    } else if (typeof exports === 'object' && typeof module === 'object') {
        // [2] CommonJS/Node.js
        factory(module['exports'] || exports);  // module.exports is for Node.js
    } else {
        // [3] No module loader (plain <script> tag) - put directly in global namespace
        factory(window['ko'] = {});
    }
}(function(koExports, amdRequire){
// Internally, all KO objects are attached to koExports (even the non-exported ones whose names will be minified by the closure compiler).
// In the future, the following "ko" variable may be made distinct from "koExports" so that private objects are not externally reachable.
var ko = typeof koExports !== 'undefined' ? koExports : {};
// Google Closure Compiler helpers (used only to make the minified file smaller)
ko.exportSymbol = function(koPath, object) {
    var tokens = koPath.split(".");

    // In the future, "ko" may become distinct from "koExports" (so that non-exported objects are not reachable)
    // At that point, "target" would be set to: (typeof koExports !== "undefined" ? koExports : ko)
    var target = ko;

    for (var i = 0; i < tokens.length - 1; i++)
        target = target[tokens[i]];
    target[tokens[tokens.length - 1]] = object;
};
ko.exportProperty = function(owner, publicName, object) {
    owner[publicName] = object;
};
ko.version = "3.5.1";

ko.exportSymbol('version', ko.version);
// For any options that may affect various areas of Knockout and aren't directly associated with data binding.
ko.options = {
    'deferUpdates': false,
    'useOnlyNativeEvents': false,
    'foreachHidesDestroyed': false
};

//ko.exportSymbol('options', ko.options);   // 'options' isn't minified
ko.utils = (function () {
    var hasOwnProperty = Object.prototype.hasOwnProperty;

    function objectForEach(obj, action) {
        for (var prop in obj) {
            if (hasOwnProperty.call(obj, prop)) {
                action(prop, obj[prop]);
            }
        }
    }

    function extend(target, source) {
        if (source) {
            for(var prop in source) {
                if(hasOwnProperty.call(source, prop)) {
                    target[prop] = source[prop];
                }
            }
        }
        return target;
    }

    function setPrototypeOf(obj, proto) {
        obj.__proto__ = proto;
        return obj;
    }

    var canSetPrototype = ({ __proto__: [] } instanceof Array);
    var canUseSymbols = !DEBUG && typeof Symbol === 'function';

    // Represent the known event types in a compact way, then at runtime transform it into a hash with event name as key (for fast lookup)
    var knownEvents = {}, knownEventTypesByEventName = {};
    var keyEventTypeName = (navigator && /Firefox\/2/i.test(navigator.userAgent)) ? 'KeyboardEvent' : 'UIEvents';
    knownEvents[keyEventTypeName] = ['keyup', 'keydown', 'keypress'];
    knownEvents['MouseEvents'] = ['click', 'dblclick', 'mousedown', 'mouseup', 'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave'];
    objectForEach(knownEvents, function(eventType, knownEventsForType) {
        if (knownEventsForType.length) {
            for (var i = 0, j = knownEventsForType.length; i < j; i++)
                knownEventTypesByEventName[knownEventsForType[i]] = eventType;
        }
    });
    var eventsThatMustBeRegisteredUsingAttachEvent = { 'propertychange': true }; // Workaround for an IE9 issue - https://github.com/SteveSanderson/knockout/issues/406

    // Detect IE versions for bug workarounds (uses IE conditionals, not UA string, for robustness)
    // Note that, since IE 10 does not support conditional comments, the following logic only detects IE < 10.
    // Currently this is by design, since IE 10+ behaves correctly when treated as a standard browser.
    // If there is a future need to detect specific versions of IE10+, we will amend this.
    var ieVersion = document && (function() {
        var version = 3, div = document.createElement('div'), iElems = div.getElementsByTagName('i');

        // Keep constructing conditional HTML blocks until we hit one that resolves to an empty fragment
        while (
            div.innerHTML = '<!--[if gt IE ' + (++version) + ']><i></i><![endif]-->',
            iElems[0]
        ) {}
        return version > 4 ? version : undefined;
    }());
    var isIe6 = ieVersion === 6,
        isIe7 = ieVersion === 7;

    function isClickOnCheckableElement(element, eventType) {
        if ((ko.utils.tagNameLower(element) !== "input") || !element.type) return false;
        if (eventType.toLowerCase() != "click") return false;
        var inputType = element.type;
        return (inputType == "checkbox") || (inputType == "radio");
    }

    // For details on the pattern for changing node classes
    // see: https://github.com/knockout/knockout/issues/1597
    var cssClassNameRegex = /\S+/g;

    var jQueryEventAttachName;

    function toggleDomNodeCssClass(node, classNames, shouldHaveClass) {
        var addOrRemoveFn;
        if (classNames) {
            if (typeof node.classList === 'object') {
                addOrRemoveFn = node.classList[shouldHaveClass ? 'add' : 'remove'];
                ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
                    addOrRemoveFn.call(node.classList, className);
                });
            } else if (typeof node.className['baseVal'] === 'string') {
                // SVG tag .classNames is an SVGAnimatedString instance
                toggleObjectClassPropertyString(node.className, 'baseVal', classNames, shouldHaveClass);
            } else {
                // node.className ought to be a string.
                toggleObjectClassPropertyString(node, 'className', classNames, shouldHaveClass);
            }
        }
    }

    function toggleObjectClassPropertyString(obj, prop, classNames, shouldHaveClass) {
        // obj/prop is either a node/'className' or a SVGAnimatedString/'baseVal'.
        var currentClassNames = obj[prop].match(cssClassNameRegex) || [];
        ko.utils.arrayForEach(classNames.match(cssClassNameRegex), function(className) {
            ko.utils.addOrRemoveItem(currentClassNames, className, shouldHaveClass);
        });
        obj[prop] = currentClassNames.join(" ");
    }

    return {
        fieldsIncludedWithJsonPost: ['authenticity_token', /^__RequestVerificationToken(_.*)?$/],

        arrayForEach: function (array, action, actionOwner) {
            for (var i = 0, j = array.length; i < j; i++) {
                action.call(actionOwner, array[i], i, array);
            }
        },

        arrayIndexOf: typeof Array.prototype.indexOf == "function"
            ? function (array, item) {
                return Array.prototype.indexOf.call(array, item);
            }
            : function (array, item) {
                for (var i = 0, j = array.length; i < j; i++) {
                    if (array[i] === item)
                        return i;
                }
                return -1;
            },

        arrayFirst: function (array, predicate, predicateOwner) {
            for (var i = 0, j = array.length; i < j; i++) {
                if (predicate.call(predicateOwner, array[i], i, array))
                    return array[i];
            }
            return undefined;
        },

        arrayRemoveItem: function (array, itemToRemove) {
            var index = ko.utils.arrayIndexOf(array, itemToRemove);
            if (index > 0) {
                array.splice(index, 1);
            }
            else if (index === 0) {
                array.shift();
            }
        },

        arrayGetDistinctValues: function (array) {
            var result = [];
            if (array) {
                ko.utils.arrayForEach(array, function(item) {
                    if (ko.utils.arrayIndexOf(result, item) < 0)
                        result.push(item);
                });
            }
            return result;
        },

        arrayMap: function (array, mapping, mappingOwner) {
            var result = [];
            if (array) {
                for (var i = 0, j = array.length; i < j; i++)
                    result.push(mapping.call(mappingOwner, array[i], i));
            }
            return result;
        },

        arrayFilter: function (array, predicate, predicateOwner) {
            var result = [];
            if (array) {
                for (var i = 0, j = array.length; i < j; i++)
                    if (predicate.call(predicateOwner, array[i], i))
                        result.push(array[i]);
            }
            return result;
        },

        arrayPushAll: function (array, valuesToPush) {
            if (valuesToPush instanceof Array)
                array.push.apply(array, valuesToPush);
            else
                for (var i = 0, j = valuesToPush.length; i < j; i++)
                    array.push(valuesToPush[i]);
            return array;
        },

        addOrRemoveItem: function(array, value, included) {
            var existingEntryIndex = ko.utils.arrayIndexOf(ko.utils.peekObservable(array), value);
            if (existingEntryIndex < 0) {
                if (included)
                    array.push(value);
            } else {
                if (!included)
                    array.splice(existingEntryIndex, 1);
            }
        },

        canSetPrototype: canSetPrototype,

        extend: extend,

        setPrototypeOf: setPrototypeOf,

        setPrototypeOfOrExtend: canSetPrototype ? setPrototypeOf : extend,

        objectForEach: objectForEach,

        objectMap: function(source, mapping, mappingOwner) {
            if (!source)
                return source;
            var target = {};
            for (var prop in source) {
                if (hasOwnProperty.call(source, prop)) {
                    target[prop] = mapping.call(mappingOwner, source[prop], prop, source);
                }
            }
            return target;
        },

        emptyDomNode: function (domNode) {
            while (domNode.firstChild) {
                ko.removeNode(domNode.firstChild);
            }
        },

        moveCleanedNodesToContainerElement: function(nodes) {
            // Ensure it's a real array, as we're about to reparent the nodes and
            // we don't want the underlying collection to change while we're doing that.
            var nodesArray = ko.utils.makeArray(nodes);
            var templateDocument = (nodesArray[0] && nodesArray[0].ownerDocument) || document;

            var container = templateDocument.createElement('div');
            for (var i = 0, j = nodesArray.length; i < j; i++) {
                container.appendChild(ko.cleanNode(nodesArray[i]));
            }
            return container;
        },

        cloneNodes: function (nodesArray, shouldCleanNodes) {
            for (var i = 0, j = nodesArray.length, newNodesArray = []; i < j; i++) {
                var clonedNode = nodesArray[i].cloneNode(true);
                newNodesArray.push(shouldCleanNodes ? ko.cleanNode(clonedNode) : clonedNode);
            }
            return newNodesArray;
        },

        setDomNodeChildren: function (domNode, childNodes) {
            ko.utils.emptyDomNode(domNode);
            if (childNodes) {
                for (var i = 0, j = childNodes.length; i < j; i++)
                    domNode.appendChild(childNodes[i]);
            }
        },

        replaceDomNodes: function (nodeToReplaceOrNodeArray, newNodesArray) {
            var nodesToReplaceArray = nodeToReplaceOrNodeArray.nodeType ? [nodeToReplaceOrNodeArray] : nodeToReplaceOrNodeArray;
            if (nodesToReplaceArray.length > 0) {
                var insertionPoint = nodesToReplaceArray[0];
                var parent = insertionPoint.parentNode;
                for (var i = 0, j = newNodesArray.length; i < j; i++)
                    parent.insertBefore(newNodesArray[i], insertionPoint);
                for (var i = 0, j = nodesToReplaceArray.length; i < j; i++) {
                    ko.removeNode(nodesToReplaceArray[i]);
                }
            }
        },

        fixUpContinuousNodeArray: function(continuousNodeArray, parentNode) {
            // Before acting on a set of nodes that were previously outputted by a template function, we have to reconcile
            // them against what is in the DOM right now. It may be that some of the nodes have already been removed, or that
            // new nodes might have been inserted in the middle, for example by a binding. Also, there may previously have been
            // leading comment nodes (created by rewritten string-based templates) that have since been removed during binding.
            // So, this function translates the old "map" output array into its best guess of the set of current DOM nodes.
            //
            // Rules:
            //   [A] Any leading nodes that have been removed should be ignored
            //       These most likely correspond to memoization nodes that were already removed during binding
            //       See https://github.com/knockout/knockout/pull/440
            //   [B] Any trailing nodes that have been remove should be ignored
            //       This prevents the code here from adding unrelated nodes to the array while processing rule [C]
            //       See https://github.com/knockout/knockout/pull/1903
            //   [C] We want to output a continuous series of nodes. So, ignore any nodes that have already been removed,
            //       and include any nodes that have been inserted among the previous collection

            if (continuousNodeArray.length) {
                // The parent node can be a virtual element; so get the real parent node
                parentNode = (parentNode.nodeType === 8 && parentNode.parentNode) || parentNode;

                // Rule [A]
                while (continuousNodeArray.length && continuousNodeArray[0].parentNode !== parentNode)
                    continuousNodeArray.splice(0, 1);

                // Rule [B]
                while (continuousNodeArray.length > 1 && continuousNodeArray[continuousNodeArray.length - 1].parentNode !== parentNode)
                    continuousNodeArray.length--;

                // Rule [C]
                if (continuousNodeArray.length > 1) {
                    var current = continuousNodeArray[0], last = continuousNodeArray[continuousNodeArray.length - 1];
                    // Replace with the actual new continuous node set
                    continuousNodeArray.length = 0;
                    while (current !== last) {
                        continuousNodeArray.push(current);
                        current = current.nextSibling;
                    }
                    continuousNodeArray.push(last);
                }
            }
            return continuousNodeArray;
        },

        setOptionNodeSelectionState: function (optionNode, isSelected) {
            // IE6 sometimes throws "unknown error" if you try to write to .selected directly, whereas Firefox struggles with setAttribute. Pick one based on browser.
            if (ieVersion < 7)
                optionNode.setAttribute("selected", isSelected);
            else
                optionNode.selected = isSelected;
        },

        stringTrim: function (string) {
            return string === null || string === undefined ? '' :
                string.trim ?
                    string.trim() :
                    string.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g, '');
        },

        stringStartsWith: function (string, startsWith) {
            string = string || "";
            if (startsWith.length > string.length)
                return false;
            return string.substring(0, startsWith.length) === startsWith;
        },

        domNodeIsContainedBy: function (node, containedByNode) {
            if (node === containedByNode)
                return true;
            if (node.nodeType === 11)
                return false; // Fixes issue #1162 - can't use node.contains for document fragments on IE8
            if (containedByNode.contains)
                return containedByNode.contains(node.nodeType !== 1 ? node.parentNode : node);
            if (containedByNode.compareDocumentPosition)
                return (containedByNode.compareDocumentPosition(node) & 16) == 16;
            while (node && node != containedByNode) {
                node = node.parentNode;
            }
            return !!node;
        },

        domNodeIsAttachedToDocument: function (node) {
            return ko.utils.domNodeIsContainedBy(node, node.ownerDocument.documentElement);
        },

        anyDomNodeIsAttachedToDocument: function(nodes) {
            return !!ko.utils.arrayFirst(nodes, ko.utils.domNodeIsAttachedToDocument);
        },

        tagNameLower: function(element) {
            // For HTML elements, tagName will always be upper case; for XHTML elements, it'll be lower case.
            // Possible future optimization: If we know it's an element from an XHTML document (not HTML),
            // we don't need to do the .toLowerCase() as it will always be lower case anyway.
            return element && element.tagName && element.tagName.toLowerCase();
        },

        catchFunctionErrors: function (delegate) {
            return ko['onError'] ? function () {
                try {
                    return delegate.apply(this, arguments);
                } catch (e) {
                    ko['onError'] && ko['onError'](e);
                    throw e;
                }
            } : delegate;
        },

        setTimeout: function (handler, timeout) {
            return setTimeout(ko.utils.catchFunctionErrors(handler), timeout);
        },

        deferError: function (error) {
            setTimeout(function () {
                ko['onError'] && ko['onError'](error);
                throw error;
            }, 0);
        },

        registerEventHandler: function (element, eventType, handler) {
            var wrappedHandler = ko.utils.catchFunctionErrors(handler);

            var mustUseAttachEvent = eventsThatMustBeRegisteredUsingAttachEvent[eventType];
            if (!ko.options['useOnlyNativeEvents'] && !mustUseAttachEvent && jQueryInstance) {
                if (!jQueryEventAttachName) {
                    jQueryEventAttachName = (typeof jQueryInstance(element)['on'] == 'function') ? 'on' : 'bind';
                }
                jQueryInstance(element)[jQueryEventAttachName](eventType, wrappedHandler);
            } else if (!mustUseAttachEvent && typeof element.addEventListener == "function")
                element.addEventListener(eventType, wrappedHandler, false);
            else if (typeof element.attachEvent != "undefined") {
                var attachEventHandler = function (event) { wrappedHandler.call(element, event); },
                    attachEventName = "on" + eventType;
                element.attachEvent(attachEventName, attachEventHandler);

                // IE does not dispose attachEvent handlers automatically (unlike with addEventListener)
                // so to avoid leaks, we have to remove them manually. See bug #856
                ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                    element.detachEvent(attachEventName, attachEventHandler);
                });
            } else
                throw new Error("Browser doesn't support addEventListener or attachEvent");
        },

        triggerEvent: function (element, eventType) {
            if (!(element && element.nodeType))
                throw new Error("element must be a DOM node when calling triggerEvent");

            // For click events on checkboxes and radio buttons, jQuery toggles the element checked state *after* the
            // event handler runs instead of *before*. (This was fixed in 1.9 for checkboxes but not for radio buttons.)
            // IE doesn't change the checked state when you trigger the click event using "fireEvent".
            // In both cases, we'll use the click method instead.
            var useClickWorkaround = isClickOnCheckableElement(element, eventType);

            if (!ko.options['useOnlyNativeEvents'] && jQueryInstance && !useClickWorkaround) {
                jQueryInstance(element)['trigger'](eventType);
            } else if (typeof document.createEvent == "function") {
                if (typeof element.dispatchEvent == "function") {
                    var eventCategory = knownEventTypesByEventName[eventType] || "HTMLEvents";
                    var event = document.createEvent(eventCategory);
                    event.initEvent(eventType, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, element);
                    element.dispatchEvent(event);
                }
                else
                    throw new Error("The supplied element doesn't support dispatchEvent");
            } else if (useClickWorkaround && element.click) {
                element.click();
            } else if (typeof element.fireEvent != "undefined") {
                element.fireEvent("on" + eventType);
            } else {
                throw new Error("Browser doesn't support triggering events");
            }
        },

        unwrapObservable: function (value) {
            return ko.isObservable(value) ? value() : value;
        },

        peekObservable: function (value) {
            return ko.isObservable(value) ? value.peek() : value;
        },

        toggleDomNodeCssClass: toggleDomNodeCssClass,

        setTextContent: function(element, textContent) {
            var value = ko.utils.unwrapObservable(textContent);
            if ((value === null) || (value === undefined))
                value = "";

            // We need there to be exactly one child: a text node.
            // If there are no children, more than one, or if it's not a text node,
            // we'll clear everything and create a single text node.
            var innerTextNode = ko.virtualElements.firstChild(element);
            if (!innerTextNode || innerTextNode.nodeType != 3 || ko.virtualElements.nextSibling(innerTextNode)) {
                ko.virtualElements.setDomNodeChildren(element, [element.ownerDocument.createTextNode(value)]);
            } else {
                innerTextNode.data = value;
            }

            ko.utils.forceRefresh(element);
        },

        setElementName: function(element, name) {
            element.name = name;

            // Workaround IE 6/7 issue
            // - https://github.com/SteveSanderson/knockout/issues/197
            // - http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
            if (ieVersion <= 7) {
                try {
                    var escapedName = element.name.replace(/[&<>'"]/g, function(r){ return "&#" + r.charCodeAt(0) + ";"; });
                    element.mergeAttributes(document.createElement("<input name='" + escapedName + "'/>"), false);
                }
                catch(e) {} // For IE9 with doc mode "IE9 Standards" and browser mode "IE9 Compatibility View"
            }
        },

        forceRefresh: function(node) {
            // Workaround for an IE9 rendering bug - https://github.com/SteveSanderson/knockout/issues/209
            if (ieVersion >= 9) {
                // For text nodes and comment nodes (most likely virtual elements), we will have to refresh the container
                var elem = node.nodeType == 1 ? node : node.parentNode;
                if (elem.style)
                    elem.style.zoom = elem.style.zoom;
            }
        },

        ensureSelectElementIsRenderedCorrectly: function(selectElement) {
            // Workaround for IE9 rendering bug - it doesn't reliably display all the text in dynamically-added select boxes unless you force it to re-render by updating the width.
            // (See https://github.com/SteveSanderson/knockout/issues/312, http://stackoverflow.com/questions/5908494/select-only-shows-first-char-of-selected-option)
            // Also fixes IE7 and IE8 bug that causes selects to be zero width if enclosed by 'if' or 'with'. (See issue #839)
            if (ieVersion) {
                var originalWidth = selectElement.style.width;
                selectElement.style.width = 0;
                selectElement.style.width = originalWidth;
            }
        },

        range: function (min, max) {
            min = ko.utils.unwrapObservable(min);
            max = ko.utils.unwrapObservable(max);
            var result = [];
            for (var i = min; i <= max; i++)
                result.push(i);
            return result;
        },

        makeArray: function(arrayLikeObject) {
            var result = [];
            for (var i = 0, j = arrayLikeObject.length; i < j; i++) {
                result.push(arrayLikeObject[i]);
            };
            return result;
        },

        createSymbolOrString: function(identifier) {
            return canUseSymbols ? Symbol(identifier) : identifier;
        },

        isIe6 : isIe6,
        isIe7 : isIe7,
        ieVersion : ieVersion,

        getFormFields: function(form, fieldName) {
            var fields = ko.utils.makeArray(form.getElementsByTagName("input")).concat(ko.utils.makeArray(form.getElementsByTagName("textarea")));
            var isMatchingField = (typeof fieldName == 'string')
                ? function(field) { return field.name === fieldName }
                : function(field) { return fieldName.test(field.name) }; // Treat fieldName as regex or object containing predicate
            var matches = [];
            for (var i = fields.length - 1; i >= 0; i--) {
                if (isMatchingField(fields[i]))
                    matches.push(fields[i]);
            };
            return matches;
        },

        parseJson: function (jsonString) {
            if (typeof jsonString == "string") {
                jsonString = ko.utils.stringTrim(jsonString);
                if (jsonString) {
                    if (JSON && JSON.parse) // Use native parsing where available
                        return JSON.parse(jsonString);
                    return (new Function("return " + jsonString))(); // Fallback on less safe parsing for older browsers
                }
            }
            return null;
        },

        stringifyJson: function (data, replacer, space) {   // replacer and space are optional
            if (!JSON || !JSON.stringify)
                throw new Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
            return JSON.stringify(ko.utils.unwrapObservable(data), replacer, space);
        },

        postJson: function (urlOrForm, data, options) {
            options = options || {};
            var params = options['params'] || {};
            var includeFields = options['includeFields'] || this.fieldsIncludedWithJsonPost;
            var url = urlOrForm;

            // If we were given a form, use its 'action' URL and pick out any requested field values
            if((typeof urlOrForm == 'object') && (ko.utils.tagNameLower(urlOrForm) === "form")) {
                var originalForm = urlOrForm;
                url = originalForm.action;
                for (var i = includeFields.length - 1; i >= 0; i--) {
                    var fields = ko.utils.getFormFields(originalForm, includeFields[i]);
                    for (var j = fields.length - 1; j >= 0; j--)
                        params[fields[j].name] = fields[j].value;
                }
            }

            data = ko.utils.unwrapObservable(data);
            var form = document.createElement("form");
            form.style.display = "none";
            form.action = url;
            form.method = "post";
            for (var key in data) {
                // Since 'data' this is a model object, we include all properties including those inherited from its prototype
                var input = document.createElement("input");
                input.type = "hidden";
                input.name = key;
                input.value = ko.utils.stringifyJson(ko.utils.unwrapObservable(data[key]));
                form.appendChild(input);
            }
            objectForEach(params, function(key, value) {
                var input = document.createElement("input");
                input.type = "hidden";
                input.name = key;
                input.value = value;
                form.appendChild(input);
            });
            document.body.appendChild(form);
            options['submitter'] ? options['submitter'](form) : form.submit();
            setTimeout(function () { form.parentNode.removeChild(form); }, 0);
        }
    }
}());

ko.exportSymbol('utils', ko.utils);
ko.exportSymbol('utils.arrayForEach', ko.utils.arrayForEach);
ko.exportSymbol('utils.arrayFirst', ko.utils.arrayFirst);
ko.exportSymbol('utils.arrayFilter', ko.utils.arrayFilter);
ko.exportSymbol('utils.arrayGetDistinctValues', ko.utils.arrayGetDistinctValues);
ko.exportSymbol('utils.arrayIndexOf', ko.utils.arrayIndexOf);
ko.exportSymbol('utils.arrayMap', ko.utils.arrayMap);
ko.exportSymbol('utils.arrayPushAll', ko.utils.arrayPushAll);
ko.exportSymbol('utils.arrayRemoveItem', ko.utils.arrayRemoveItem);
ko.exportSymbol('utils.cloneNodes', ko.utils.cloneNodes);
ko.exportSymbol('utils.createSymbolOrString', ko.utils.createSymbolOrString);
ko.exportSymbol('utils.extend', ko.utils.extend);
ko.exportSymbol('utils.fieldsIncludedWithJsonPost', ko.utils.fieldsIncludedWithJsonPost);
ko.exportSymbol('utils.getFormFields', ko.utils.getFormFields);
ko.exportSymbol('utils.objectMap', ko.utils.objectMap);
ko.exportSymbol('utils.peekObservable', ko.utils.peekObservable);
ko.exportSymbol('utils.postJson', ko.utils.postJson);
ko.exportSymbol('utils.parseJson', ko.utils.parseJson);
ko.exportSymbol('utils.registerEventHandler', ko.utils.registerEventHandler);
ko.exportSymbol('utils.stringifyJson', ko.utils.stringifyJson);
ko.exportSymbol('utils.range', ko.utils.range);
ko.exportSymbol('utils.toggleDomNodeCssClass', ko.utils.toggleDomNodeCssClass);
ko.exportSymbol('utils.triggerEvent', ko.utils.triggerEvent);
ko.exportSymbol('utils.unwrapObservable', ko.utils.unwrapObservable);
ko.exportSymbol('utils.objectForEach', ko.utils.objectForEach);
ko.exportSymbol('utils.addOrRemoveItem', ko.utils.addOrRemoveItem);
ko.exportSymbol('utils.setTextContent', ko.utils.setTextContent);
ko.exportSymbol('unwrap', ko.utils.unwrapObservable); // Convenient shorthand, because this is used so commonly

if (!Function.prototype['bind']) {
    // Function.prototype.bind is a standard part of ECMAScript 5th Edition (December 2009, http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf)
    // In case the browser doesn't implement it natively, provide a JavaScript implementation. This implementation is based on the one in prototype.js
    Function.prototype['bind'] = function (object) {
        var originalFunction = this;
        if (arguments.length === 1) {
            return function () {
                return originalFunction.apply(object, arguments);
            };
        } else {
            var partialArgs = Array.prototype.slice.call(arguments, 1);
            return function () {
                var args = partialArgs.slice(0);
                args.push.apply(args, arguments);
                return originalFunction.apply(object, args);
            };
        }
    };
}

ko.utils.domData = new (function () {
    var uniqueId = 0;
    var dataStoreKeyExpandoPropertyName = "__ko__" + (new Date).getTime();
    var dataStore = {};

    var getDataForNode, clear;
    if (!ko.utils.ieVersion) {
        // We considered using WeakMap, but it has a problem in IE 11 and Edge that prevents using
        // it cross-window, so instead we just store the data directly on the node.
        // See https://github.com/knockout/knockout/issues/2141
        getDataForNode = function (node, createIfNotFound) {
            var dataForNode = node[dataStoreKeyExpandoPropertyName];
            if (!dataForNode && createIfNotFound) {
                dataForNode = node[dataStoreKeyExpandoPropertyName] = {};
            }
            return dataForNode;
        };
        clear = function (node) {
            if (node[dataStoreKeyExpandoPropertyName]) {
                delete node[dataStoreKeyExpandoPropertyName];
                return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
            }
            return false;
        };
    } else {
        // Old IE versions have memory issues if you store objects on the node, so we use a
        // separate data storage and link to it from the node using a string key.
        getDataForNode = function (node, createIfNotFound) {
            var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
            var hasExistingDataStore = dataStoreKey && (dataStoreKey !== "null") && dataStore[dataStoreKey];
            if (!hasExistingDataStore) {
                if (!createIfNotFound)
                    return undefined;
                dataStoreKey = node[dataStoreKeyExpandoPropertyName] = "ko" + uniqueId++;
                dataStore[dataStoreKey] = {};
            }
            return dataStore[dataStoreKey];
        };
        clear = function (node) {
            var dataStoreKey = node[dataStoreKeyExpandoPropertyName];
            if (dataStoreKey) {
                delete dataStore[dataStoreKey];
                node[dataStoreKeyExpandoPropertyName] = null;
                return true; // Exposing "did clean" flag purely so specs can infer whether things have been cleaned up as intended
            }
            return false;
        };
    }

    return {
        get: function (node, key) {
            var dataForNode = getDataForNode(node, false);
            return dataForNode && dataForNode[key];
        },
        set: function (node, key, value) {
            // Make sure we don't actually create a new domData key if we are actually deleting a value
            var dataForNode = getDataForNode(node, value !== undefined /* createIfNotFound */);
            dataForNode && (dataForNode[key] = value);
        },
        getOrSet: function (node, key, value) {
            var dataForNode = getDataForNode(node, true /* createIfNotFound */);
            return dataForNode[key] || (dataForNode[key] = value);
        },
        clear: clear,

        nextKey: function () {
            return (uniqueId++) + dataStoreKeyExpandoPropertyName;
        }
    };
})();

ko.exportSymbol('utils.domData', ko.utils.domData);
ko.exportSymbol('utils.domData.clear', ko.utils.domData.clear); // Exporting only so specs can clear up after themselves fully

ko.utils.domNodeDisposal = new (function () {
    var domDataKey = ko.utils.domData.nextKey();
    var cleanableNodeTypes = { 1: true, 8: true, 9: true };       // Element, Comment, Document
    var cleanableNodeTypesWithDescendants = { 1: true, 9: true }; // Element, Document

    function getDisposeCallbacksCollection(node, createIfNotFound) {
        var allDisposeCallbacks = ko.utils.domData.get(node, domDataKey);
        if ((allDisposeCallbacks === undefined) && createIfNotFound) {
            allDisposeCallbacks = [];
            ko.utils.domData.set(node, domDataKey, allDisposeCallbacks);
        }
        return allDisposeCallbacks;
    }
    function destroyCallbacksCollection(node) {
        ko.utils.domData.set(node, domDataKey, undefined);
    }

    function cleanSingleNode(node) {
        // Run all the dispose callbacks
        var callbacks = getDisposeCallbacksCollection(node, false);
        if (callbacks) {
            callbacks = callbacks.slice(0); // Clone, as the array may be modified during iteration (typically, callbacks will remove themselves)
            for (var i = 0; i < callbacks.length; i++)
                callbacks[i](node);
        }

        // Erase the DOM data
        ko.utils.domData.clear(node);

        // Perform cleanup needed by external libraries (currently only jQuery, but can be extended)
        ko.utils.domNodeDisposal["cleanExternalData"](node);

        // Clear any immediate-child comment nodes, as these wouldn't have been found by
        // node.getElementsByTagName("*") in cleanNode() (comment nodes aren't elements)
        if (cleanableNodeTypesWithDescendants[node.nodeType]) {
            cleanNodesInList(node.childNodes, true/*onlyComments*/);
        }
    }

    function cleanNodesInList(nodeList, onlyComments) {
        var cleanedNodes = [], lastCleanedNode;
        for (var i = 0; i < nodeList.length; i++) {
            if (!onlyComments || nodeList[i].nodeType === 8) {
                cleanSingleNode(cleanedNodes[cleanedNodes.length] = lastCleanedNode = nodeList[i]);
                if (nodeList[i] !== lastCleanedNode) {
                    while (i-- && ko.utils.arrayIndexOf(cleanedNodes, nodeList[i]) == -1) {}
                }
            }
        }
    }

    return {
        addDisposeCallback : function(node, callback) {
            if (typeof callback != "function")
                throw new Error("Callback must be a function");
            getDisposeCallbacksCollection(node, true).push(callback);
        },

        removeDisposeCallback : function(node, callback) {
            var callbacksCollection = getDisposeCallbacksCollection(node, false);
            if (callbacksCollection) {
                ko.utils.arrayRemoveItem(callbacksCollection, callback);
                if (callbacksCollection.length == 0)
                    destroyCallbacksCollection(node);
            }
        },

        cleanNode : function(node) {
            ko.dependencyDetection.ignore(function () {
                // First clean this node, where applicable
                if (cleanableNodeTypes[node.nodeType]) {
                    cleanSingleNode(node);

                    // ... then its descendants, where applicable
                    if (cleanableNodeTypesWithDescendants[node.nodeType]) {
                        cleanNodesInList(node.getElementsByTagName("*"));
                    }
                }
            });

            return node;
        },

        removeNode : function(node) {
            ko.cleanNode(node);
            if (node.parentNode)
                node.parentNode.removeChild(node);
        },

        "cleanExternalData" : function (node) {
            // Special support for jQuery here because it's so commonly used.
            // Many jQuery plugins (including jquery.tmpl) store data using jQuery's equivalent of domData
            // so notify it to tear down any resources associated with the node & descendants here.
            if (jQueryInstance && (typeof jQueryInstance['cleanData'] == "function"))
                jQueryInstance['cleanData']([node]);
        }
    };
})();
ko.cleanNode = ko.utils.domNodeDisposal.cleanNode; // Shorthand name for convenience
ko.removeNode = ko.utils.domNodeDisposal.removeNode; // Shorthand name for convenience
ko.exportSymbol('cleanNode', ko.cleanNode);
ko.exportSymbol('removeNode', ko.removeNode);
ko.exportSymbol('utils.domNodeDisposal', ko.utils.domNodeDisposal);
ko.exportSymbol('utils.domNodeDisposal.addDisposeCallback', ko.utils.domNodeDisposal.addDisposeCallback);
ko.exportSymbol('utils.domNodeDisposal.removeDisposeCallback', ko.utils.domNodeDisposal.removeDisposeCallback);
(function () {
    var none = [0, "", ""],
        table = [1, "<table>", "</table>"],
        tbody = [2, "<table><tbody>", "</tbody></table>"],
        tr = [3, "<table><tbody><tr>", "</tr></tbody></table>"],
        select = [1, "<select multiple='multiple'>", "</select>"],
        lookup = {
            'thead': table,
            'tbody': table,
            'tfoot': table,
            'tr': tbody,
            'td': tr,
            'th': tr,
            'option': select,
            'optgroup': select
        },

        // This is needed for old IE if you're *not* using either jQuery or innerShiv. Doesn't affect other cases.
        mayRequireCreateElementHack = ko.utils.ieVersion <= 8;

    function getWrap(tags) {
        var m = tags.match(/^(?:<!--.*?-->\s*?)*?<([a-z]+)[\s>]/);
        return (m && lookup[m[1]]) || none;
    }

    function simpleHtmlParse(html, documentContext) {
        documentContext || (documentContext = document);
        var windowContext = documentContext['parentWindow'] || documentContext['defaultView'] || window;

        // Based on jQuery's "clean" function, but only accounting for table-related elements.
        // If you have referenced jQuery, this won't be used anyway - KO will use jQuery's "clean" function directly

        // Note that there's still an issue in IE < 9 whereby it will discard comment nodes that are the first child of
        // a descendant node. For example: "<div><!-- mycomment -->abc</div>" will get parsed as "<div>abc</div>"
        // This won't affect anyone who has referenced jQuery, and there's always the workaround of inserting a dummy node
        // (possibly a text node) in front of the comment. So, KO does not attempt to workaround this IE issue automatically at present.

        // Trim whitespace, otherwise indexOf won't work as expected
        var tags = ko.utils.stringTrim(html).toLowerCase(), div = documentContext.createElement("div"),
            wrap = getWrap(tags),
            depth = wrap[0];

        // Go to html and back, then peel off extra wrappers
        // Note that we always prefix with some dummy text, because otherwise, IE<9 will strip out leading comment nodes in descendants. Total madness.
        var markup = "ignored<div>" + wrap[1] + html + wrap[2] + "</div>";
        if (typeof windowContext['innerShiv'] == "function") {
            // Note that innerShiv is deprecated in favour of html5shiv. We should consider adding
            // support for html5shiv (except if no explicit support is needed, e.g., if html5shiv
            // somehow shims the native APIs so it just works anyway)
            div.appendChild(windowContext['innerShiv'](markup));
        } else {
            if (mayRequireCreateElementHack) {
                // The document.createElement('my-element') trick to enable custom elements in IE6-8
                // only works if we assign innerHTML on an element associated with that document.
                documentContext.body.appendChild(div);
            }

            div.innerHTML = markup;

            if (mayRequireCreateElementHack) {
                div.parentNode.removeChild(div);
            }
        }

        // Move to the right depth
        while (depth--)
            div = div.lastChild;

        return ko.utils.makeArray(div.lastChild.childNodes);
    }

    function jQueryHtmlParse(html, documentContext) {
        // jQuery's "parseHTML" function was introduced in jQuery 1.8.0 and is a documented public API.
        if (jQueryInstance['parseHTML']) {
            return jQueryInstance['parseHTML'](html, documentContext) || []; // Ensure we always return an array and never null
        } else {
            // For jQuery < 1.8.0, we fall back on the undocumented internal "clean" function.
            var elems = jQueryInstance['clean']([html], documentContext);

            // As of jQuery 1.7.1, jQuery parses the HTML by appending it to some dummy parent nodes held in an in-memory document fragment.
            // Unfortunately, it never clears the dummy parent nodes from the document fragment, so it leaks memory over time.
            // Fix this by finding the top-most dummy parent element, and detaching it from its owner fragment.
            if (elems && elems[0]) {
                // Find the top-most parent element that's a direct child of a document fragment
                var elem = elems[0];
                while (elem.parentNode && elem.parentNode.nodeType !== 11 /* i.e., DocumentFragment */)
                    elem = elem.parentNode;
                // ... then detach it
                if (elem.parentNode)
                    elem.parentNode.removeChild(elem);
            }

            return elems;
        }
    }

    ko.utils.parseHtmlFragment = function(html, documentContext) {
        return jQueryInstance ?
            jQueryHtmlParse(html, documentContext) :   // As below, benefit from jQuery's optimisations where possible
            simpleHtmlParse(html, documentContext);  // ... otherwise, this simple logic will do in most common cases.
    };

    ko.utils.parseHtmlForTemplateNodes = function(html, documentContext) {
        var nodes = ko.utils.parseHtmlFragment(html, documentContext);
        return (nodes.length && nodes[0].parentElement) || ko.utils.moveCleanedNodesToContainerElement(nodes);
    };

    ko.utils.setHtml = function(node, html) {
        ko.utils.emptyDomNode(node);

        // There's no legitimate reason to display a stringified observable without unwrapping it, so we'll unwrap it
        html = ko.utils.unwrapObservable(html);

        if ((html !== null) && (html !== undefined)) {
            if (typeof html != 'string')
                html = html.toString();

            // jQuery contains a lot of sophisticated code to parse arbitrary HTML fragments,
            // for example <tr> elements which are not normally allowed to exist on their own.
            // If you've referenced jQuery we'll use that rather than duplicating its code.
            if (jQueryInstance) {
                jQueryInstance(node)['html'](html);
            } else {
                // ... otherwise, use KO's own parsing logic.
                var parsedNodes = ko.utils.parseHtmlFragment(html, node.ownerDocument);
                for (var i = 0; i < parsedNodes.length; i++)
                    node.appendChild(parsedNodes[i]);
            }
        }
    };
})();

ko.exportSymbol('utils.parseHtmlFragment', ko.utils.parseHtmlFragment);
ko.exportSymbol('utils.setHtml', ko.utils.setHtml);

ko.memoization = (function () {
    var memos = {};

    function randomMax8HexChars() {
        return (((1 + Math.random()) * 0x100000000) | 0).toString(16).substring(1);
    }
    function generateRandomId() {
        return randomMax8HexChars() + randomMax8HexChars();
    }
    function findMemoNodes(rootNode, appendToArray) {
        if (!rootNode)
            return;
        if (rootNode.nodeType == 8) {
            var memoId = ko.memoization.parseMemoText(rootNode.nodeValue);
            if (memoId != null)
                appendToArray.push({ domNode: rootNode, memoId: memoId });
        } else if (rootNode.nodeType == 1) {
            for (var i = 0, childNodes = rootNode.childNodes, j = childNodes.length; i < j; i++)
                findMemoNodes(childNodes[i], appendToArray);
        }
    }

    return {
        memoize: function (callback) {
            if (typeof callback != "function")
                throw new Error("You can only pass a function to ko.memoization.memoize()");
            var memoId = generateRandomId();
            memos[memoId] = callback;
            return "<!--[ko_memo:" + memoId + "]-->";
        },

        unmemoize: function (memoId, callbackParams) {
            var callback = memos[memoId];
            if (callback === undefined)
                throw new Error("Couldn't find any memo with ID " + memoId + ". Perhaps it's already been unmemoized.");
            try {
                callback.apply(null, callbackParams || []);
                return true;
            }
            finally { delete memos[memoId]; }
        },

        unmemoizeDomNodeAndDescendants: function (domNode, extraCallbackParamsArray) {
            var memos = [];
            findMemoNodes(domNode, memos);
            for (var i = 0, j = memos.length; i < j; i++) {
                var node = memos[i].domNode;
                var combinedParams = [node];
                if (extraCallbackParamsArray)
                    ko.utils.arrayPushAll(combinedParams, extraCallbackParamsArray);
                ko.memoization.unmemoize(memos[i].memoId, combinedParams);
                node.nodeValue = ""; // Neuter this node so we don't try to unmemoize it again
                if (node.parentNode)
                    node.parentNode.removeChild(node); // If possible, erase it totally (not always possible - someone else might just hold a reference to it then call unmemoizeDomNodeAndDescendants again)
            }
        },

        parseMemoText: function (memoText) {
            var match = memoText.match(/^\[ko_memo\:(.*?)\]$/);
            return match ? match[1] : null;
        }
    };
})();

ko.exportSymbol('memoization', ko.memoization);
ko.exportSymbol('memoization.memoize', ko.memoization.memoize);
ko.exportSymbol('memoization.unmemoize', ko.memoization.unmemoize);
ko.exportSymbol('memoization.parseMemoText', ko.memoization.parseMemoText);
ko.exportSymbol('memoization.unmemoizeDomNodeAndDescendants', ko.memoization.unmemoizeDomNodeAndDescendants);
ko.tasks = (function () {
    var scheduler,
        taskQueue = [],
        taskQueueLength = 0,
        nextHandle = 1,
        nextIndexToProcess = 0;

    if (window['MutationObserver']) {
        // Chrome 27+, Firefox 14+, IE 11+, Opera 15+, Safari 6.1+
        // From https://github.com/petkaantonov/bluebird * Copyright (c) 2014 Petka Antonov * License: MIT
        scheduler = (function (callback) {
            var div = document.createElement("div");
            new MutationObserver(callback).observe(div, {attributes: true});
            return function () { div.classList.toggle("foo"); };
        })(scheduledProcess);
    } else if (document && "onreadystatechange" in document.createElement("script")) {
        // IE 6-10
        // From https://github.com/YuzuJS/setImmediate * Copyright (c) 2012 Barnesandnoble.com, llc, Donavon West, and Domenic Denicola * License: MIT
        scheduler = function (callback) {
            var script = document.createElement("script");
            script.onreadystatechange = function () {
                script.onreadystatechange = null;
                document.documentElement.removeChild(script);
                script = null;
                callback();
            };
            document.documentElement.appendChild(script);
        };
    } else {
        scheduler = function (callback) {
            setTimeout(callback, 0);
        };
    }

    function processTasks() {
        if (taskQueueLength) {
            // Each mark represents the end of a logical group of tasks and the number of these groups is
            // limited to prevent unchecked recursion.
            var mark = taskQueueLength, countMarks = 0;

            // nextIndexToProcess keeps track of where we are in the queue; processTasks can be called recursively without issue
            for (var task; nextIndexToProcess < taskQueueLength; ) {
                if (task = taskQueue[nextIndexToProcess++]) {
                    if (nextIndexToProcess > mark) {
                        if (++countMarks >= 5000) {
                            nextIndexToProcess = taskQueueLength;   // skip all tasks remaining in the queue since any of them could be causing the recursion
                            ko.utils.deferError(Error("'Too much recursion' after processing " + countMarks + " task groups."));
                            break;
                        }
                        mark = taskQueueLength;
                    }
                    try {
                        task();
                    } catch (ex) {
                        ko.utils.deferError(ex);
                    }
                }
            }
        }
    }

    function scheduledProcess() {
        processTasks();

        // Reset the queue
        nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
    }

    function scheduleTaskProcessing() {
        ko.tasks['scheduler'](scheduledProcess);
    }

    var tasks = {
        'scheduler': scheduler,     // Allow overriding the scheduler

        schedule: function (func) {
            if (!taskQueueLength) {
                scheduleTaskProcessing();
            }

            taskQueue[taskQueueLength++] = func;
            return nextHandle++;
        },

        cancel: function (handle) {
            var index = handle - (nextHandle - taskQueueLength);
            if (index >= nextIndexToProcess && index < taskQueueLength) {
                taskQueue[index] = null;
            }
        },

        // For testing only: reset the queue and return the previous queue length
        'resetForTesting': function () {
            var length = taskQueueLength - nextIndexToProcess;
            nextIndexToProcess = taskQueueLength = taskQueue.length = 0;
            return length;
        },

        runEarly: processTasks
    };

    return tasks;
})();

ko.exportSymbol('tasks', ko.tasks);
ko.exportSymbol('tasks.schedule', ko.tasks.schedule);
//ko.exportSymbol('tasks.cancel', ko.tasks.cancel);  "cancel" isn't minified
ko.exportSymbol('tasks.runEarly', ko.tasks.runEarly);
ko.extenders = {
    'throttle': function(target, timeout) {
        // Throttling means two things:

        // (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
        //     notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
        target['throttleEvaluation'] = timeout;

        // (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
        //     so the target cannot change value synchronously or faster than a certain rate
        var writeTimeoutInstance = null;
        return ko.dependentObservable({
            'read': target,
            'write': function(value) {
                clearTimeout(writeTimeoutInstance);
                writeTimeoutInstance = ko.utils.setTimeout(function() {
                    target(value);
                }, timeout);
            }
        });
    },

    'rateLimit': function(target, options) {
        var timeout, method, limitFunction;

        if (typeof options == 'number') {
            timeout = options;
        } else {
            timeout = options['timeout'];
            method = options['method'];
        }

        // rateLimit supersedes deferred updates
        target._deferUpdates = false;

        limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ?  debounce : throttle;
        target.limit(function(callback) {
            return limitFunction(callback, timeout, options);
        });
    },

    'deferred': function(target, options) {
        if (options !== true) {
            throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.')
        }

        if (!target._deferUpdates) {
            target._deferUpdates = true;
            target.limit(function (callback) {
                var handle,
                    ignoreUpdates = false;
                return function () {
                    if (!ignoreUpdates) {
                        ko.tasks.cancel(handle);
                        handle = ko.tasks.schedule(callback);

                        try {
                            ignoreUpdates = true;
                            target['notifySubscribers'](undefined, 'dirty');
                        } finally {
                            ignoreUpdates = false;
                        }
                    }
                };
            });
        }
    },

    'notify': function(target, notifyWhen) {
        target["equalityComparer"] = notifyWhen == "always" ?
            null :  // null equalityComparer means to always notify
            valuesArePrimitiveAndEqual;
    }
};

var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 };
function valuesArePrimitiveAndEqual(a, b) {
    var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
    return oldValueIsPrimitive ? (a === b) : false;
}

function throttle(callback, timeout) {
    var timeoutInstance;
    return function () {
        if (!timeoutInstance) {
            timeoutInstance = ko.utils.setTimeout(function () {
                timeoutInstance = undefined;
                callback();
            }, timeout);
        }
    };
}

function debounce(callback, timeout) {
    var timeoutInstance;
    return function () {
        clearTimeout(timeoutInstance);
        timeoutInstance = ko.utils.setTimeout(callback, timeout);
    };
}

function applyExtenders(requestedExtenders) {
    var target = this;
    if (requestedExtenders) {
        ko.utils.objectForEach(requestedExtenders, function(key, value) {
            var extenderHandler = ko.extenders[key];
            if (typeof extenderHandler == 'function') {
                target = extenderHandler(target, value) || target;
            }
        });
    }
    return target;
}

ko.exportSymbol('extenders', ko.extenders);

ko.subscription = function (target, callback, disposeCallback) {
    this._target = target;
    this._callback = callback;
    this._disposeCallback = disposeCallback;
    this._isDisposed = false;
    this._node = null;
    this._domNodeDisposalCallback = null;
    ko.exportProperty(this, 'dispose', this.dispose);
    ko.exportProperty(this, 'disposeWhenNodeIsRemoved', this.disposeWhenNodeIsRemoved);
};
ko.subscription.prototype.dispose = function () {
    var self = this;
    if (!self._isDisposed) {
        if (self._domNodeDisposalCallback) {
            ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);
        }
        self._isDisposed = true;
        self._disposeCallback();

        self._target = self._callback = self._disposeCallback = self._node = self._domNodeDisposalCallback = null;
    }
};
ko.subscription.prototype.disposeWhenNodeIsRemoved = function (node) {
    this._node = node;
    ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this));
};

ko.subscribable = function () {
    ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);
    ko_subscribable_fn.init(this);
}

var defaultEvent = "change";

// Moved out of "limit" to avoid the extra closure
function limitNotifySubscribers(value, event) {
    if (!event || event === defaultEvent) {
        this._limitChange(value);
    } else if (event === 'beforeChange') {
        this._limitBeforeChange(value);
    } else {
        this._origNotifySubscribers(value, event);
    }
}

var ko_subscribable_fn = {
    init: function(instance) {
        instance._subscriptions = { "change": [] };
        instance._versionNumber = 1;
    },

    subscribe: function (callback, callbackTarget, event) {
        var self = this;

        event = event || defaultEvent;
        var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;

        var subscription = new ko.subscription(self, boundCallback, function () {
            ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
            if (self.afterSubscriptionRemove)
                self.afterSubscriptionRemove(event);
        });

        if (self.beforeSubscriptionAdd)
            self.beforeSubscriptionAdd(event);

        if (!self._subscriptions[event])
            self._subscriptions[event] = [];
        self._subscriptions[event].push(subscription);

        return subscription;
    },

    "notifySubscribers": function (valueToNotify, event) {
        event = event || defaultEvent;
        if (event === defaultEvent) {
            this.updateVersion();
        }
        if (this.hasSubscriptionsForEvent(event)) {
            var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);
            try {
                ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
                for (var i = 0, subscription; subscription = subs[i]; ++i) {
                    // In case a subscription was disposed during the arrayForEach cycle, check
                    // for isDisposed on each subscription before invoking its callback
                    if (!subscription._isDisposed)
                        subscription._callback(valueToNotify);
                }
            } finally {
                ko.dependencyDetection.end(); // End suppressing dependency detection
            }
        }
    },

    getVersion: function () {
        return this._versionNumber;
    },

    hasChanged: function (versionToCheck) {
        return this.getVersion() !== versionToCheck;
    },

    updateVersion: function () {
        ++this._versionNumber;
    },

    limit: function(limitFunction) {
        var self = this, selfIsObservable = ko.isObservable(self),
            ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,
            beforeChange = 'beforeChange';

        if (!self._origNotifySubscribers) {
            self._origNotifySubscribers = self["notifySubscribers"];
            self["notifySubscribers"] = limitNotifySubscribers;
        }

        var finish = limitFunction(function() {
            self._notificationIsPending = false;

            // If an observable provided a reference to itself, access it to get the latest value.
            // This allows computed observables to delay calculating their value until needed.
            if (selfIsObservable && pendingValue === self) {
                pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
            }
            var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));

            didUpdate = notifyNextChange = ignoreBeforeChange = false;

            if (shouldNotify) {
                self._origNotifySubscribers(previousValue = pendingValue);
            }
        });

        self._limitChange = function(value, isDirty) {
            if (!isDirty || !self._notificationIsPending) {
                didUpdate = !isDirty;
            }
            self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);
            self._notificationIsPending = ignoreBeforeChange = true;
            pendingValue = value;
            finish();
        };
        self._limitBeforeChange = function(value) {
            if (!ignoreBeforeChange) {
                previousValue = value;
                self._origNotifySubscribers(value, beforeChange);
            }
        };
        self._recordUpdate = function() {
            didUpdate = true;
        };
        self._notifyNextChangeIfValueIsDifferent = function() {
            if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
                notifyNextChange = true;
            }
        };
    },

    hasSubscriptionsForEvent: function(event) {
        return this._subscriptions[event] && this._subscriptions[event].length;
    },

    getSubscriptionsCount: function (event) {
        if (event) {
            return this._subscriptions[event] && this._subscriptions[event].length || 0;
        } else {
            var total = 0;
            ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
                if (eventName !== 'dirty')
                    total += subscriptions.length;
            });
            return total;
        }
    },

    isDifferent: function(oldValue, newValue) {
        return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
    },

    toString: function() {
      return '[object Object]'
    },

    extend: applyExtenders
};

ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init);
ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe);
ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend);
ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount);

// For browsers that support proto assignment, we overwrite the prototype of each
// observable instance. Since observables are functions, we need Function.prototype
// to still be in the prototype chain.
if (ko.utils.canSetPrototype) {
    ko.utils.setPrototypeOf(ko_subscribable_fn, Function.prototype);
}

ko.subscribable['fn'] = ko_subscribable_fn;


ko.isSubscribable = function (instance) {
    return instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
};

ko.exportSymbol('subscribable', ko.subscribable);
ko.exportSymbol('isSubscribable', ko.isSubscribable);

ko.computedContext = ko.dependencyDetection = (function () {
    var outerFrames = [],
        currentFrame,
        lastId = 0;

    // Return a unique ID that can be assigned to an observable for dependency tracking.
    // Theoretically, you could eventually overflow the number storage size, resulting
    // in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
    // or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
    // take over 285 years to reach that number.
    // Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
    function getId() {
        return ++lastId;
    }

    function begin(options) {
        outerFrames.push(currentFrame);
        currentFrame = options;
    }

    function end() {
        currentFrame = outerFrames.pop();
    }

    return {
        begin: begin,

        end: end,

        registerDependency: function (subscribable) {
            if (currentFrame) {
                if (!ko.isSubscribable(subscribable))
                    throw new Error("Only subscribable things can act as dependencies");
                currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
            }
        },

        ignore: function (callback, callbackTarget, callbackArgs) {
            try {
                begin();
                return callback.apply(callbackTarget, callbackArgs || []);
            } finally {
                end();
            }
        },

        getDependenciesCount: function () {
            if (currentFrame)
                return currentFrame.computed.getDependenciesCount();
        },

        getDependencies: function () {
            if (currentFrame)
                return currentFrame.computed.getDependencies();
        },

        isInitial: function() {
            if (currentFrame)
                return currentFrame.isInitial;
        },

        computed: function() {
            if (currentFrame)
                return currentFrame.computed;
        }
    };
})();

ko.exportSymbol('computedContext', ko.computedContext);
ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);
ko.exportSymbol('computedContext.getDependencies', ko.computedContext.getDependencies);
ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);
ko.exportSymbol('computedContext.registerDependency', ko.computedContext.registerDependency);

ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);
var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');

ko.observable = function (initialValue) {
    function observable() {
        if (arguments.length > 0) {
            // Write

            // Ignore writes if the value hasn't changed
            if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
                observable.valueWillMutate();
                observable[observableLatestValue] = arguments[0];
                observable.valueHasMutated();
            }
            return this; // Permits chained assignments
        }
        else {
            // Read
            ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
            return observable[observableLatestValue];
        }
    }

    observable[observableLatestValue] = initialValue;

    // Inherit from 'subscribable'
    if (!ko.utils.canSetPrototype) {
        // 'subscribable' won't be on the prototype chain unless we put it there directly
        ko.utils.extend(observable, ko.subscribable['fn']);
    }
    ko.subscribable['fn'].init(observable);

    // Inherit from 'observable'
    ko.utils.setPrototypeOfOrExtend(observable, observableFn);

    if (ko.options['deferUpdates']) {
        ko.extenders['deferred'](observable, true);
    }

    return observable;
}

// Define prototype for observables
var observableFn = {
    'equalityComparer': valuesArePrimitiveAndEqual,
    peek: function() { return this[observableLatestValue]; },
    valueHasMutated: function () {
        this['notifySubscribers'](this[observableLatestValue], 'spectate');
        this['notifySubscribers'](this[observableLatestValue]);
    },
    valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }
};

// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observable constructor
if (ko.utils.canSetPrototype) {
    ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);
}

var protoProperty = ko.observable.protoProperty = '__ko_proto__';
observableFn[protoProperty] = ko.observable;

ko.isObservable = function (instance) {
    var proto = typeof instance == 'function' && instance[protoProperty];
    if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) {
        throw Error("Invalid object that looks like an observable; possibly from another Knockout instance");
    }
    return !!proto;
};

ko.isWriteableObservable = function (instance) {
    return (typeof instance == 'function' && (
        (instance[protoProperty] === observableFn[protoProperty]) ||  // Observable
        (instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction)));   // Writable computed observable
};

ko.exportSymbol('observable', ko.observable);
ko.exportSymbol('isObservable', ko.isObservable);
ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
ko.exportSymbol('isWritableObservable', ko.isWriteableObservable);
ko.exportSymbol('observable.fn', observableFn);
ko.exportProperty(observableFn, 'peek', observableFn.peek);
ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);
ko.exportProperty(observableFn, 'valueWillMutate', observableFn.valueWillMutate);
ko.observableArray = function (initialValues) {
    initialValues = initialValues || [];

    if (typeof initialValues != 'object' || !('length' in initialValues))
        throw new Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");

    var result = ko.observable(initialValues);
    ko.utils.setPrototypeOfOrExtend(result, ko.observableArray['fn']);
    return result.extend({'trackArrayChanges':true});
};

ko.observableArray['fn'] = {
    'remove': function (valueOrPredicate) {
        var underlyingArray = this.peek();
        var removedValues = [];
        var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
        for (var i = 0; i < underlyingArray.length; i++) {
            var value = underlyingArray[i];
            if (predicate(value)) {
                if (removedValues.length === 0) {
                    this.valueWillMutate();
                }
                if (underlyingArray[i] !== value) {
                    throw Error("Array modified during remove; cannot remove item");
                }
                removedValues.push(value);
                underlyingArray.splice(i, 1);
                i--;
            }
        }
        if (removedValues.length) {
            this.valueHasMutated();
        }
        return removedValues;
    },

    'removeAll': function (arrayOfValues) {
        // If you passed zero args, we remove everything
        if (arrayOfValues === undefined) {
            var underlyingArray = this.peek();
            var allValues = underlyingArray.slice(0);
            this.valueWillMutate();
            underlyingArray.splice(0, underlyingArray.length);
            this.valueHasMutated();
            return allValues;
        }
        // If you passed an arg, we interpret it as an array of entries to remove
        if (!arrayOfValues)
            return [];
        return this['remove'](function (value) {
            return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
        });
    },

    'destroy': function (valueOrPredicate) {
        var underlyingArray = this.peek();
        var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
        this.valueWillMutate();
        for (var i = underlyingArray.length - 1; i >= 0; i--) {
            var value = underlyingArray[i];
            if (predicate(value))
                value["_destroy"] = true;
        }
        this.valueHasMutated();
    },

    'destroyAll': function (arrayOfValues) {
        // If you passed zero args, we destroy everything
        if (arrayOfValues === undefined)
            return this['destroy'](function() { return true });

        // If you passed an arg, we interpret it as an array of entries to destroy
        if (!arrayOfValues)
            return [];
        return this['destroy'](function (value) {
            return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
        });
    },

    'indexOf': function (item) {
        var underlyingArray = this();
        return ko.utils.arrayIndexOf(underlyingArray, item);
    },

    'replace': function(oldItem, newItem) {
        var index = this['indexOf'](oldItem);
        if (index >= 0) {
            this.valueWillMutate();
            this.peek()[index] = newItem;
            this.valueHasMutated();
        }
    },

    'sorted': function (compareFunction) {
        var arrayCopy = this().slice(0);
        return compareFunction ? arrayCopy.sort(compareFunction) : arrayCopy.sort();
    },

    'reversed': function () {
        return this().slice(0).reverse();
    }
};

// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observableArray constructor
if (ko.utils.canSetPrototype) {
    ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);
}

// Populate ko.observableArray.fn with read/write functions from native arrays
// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
    ko.observableArray['fn'][methodName] = function () {
        // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
        // (for consistency with mutating regular observables)
        var underlyingArray = this.peek();
        this.valueWillMutate();
        this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
        var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
        this.valueHasMutated();
        // The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
        return methodCallResult === underlyingArray ? this : methodCallResult;
    };
});

// Populate ko.observableArray.fn with read-only functions from native arrays
ko.utils.arrayForEach(["slice"], function (methodName) {
    ko.observableArray['fn'][methodName] = function () {
        var underlyingArray = this();
        return underlyingArray[methodName].apply(underlyingArray, arguments);
    };
});

ko.isObservableArray = function (instance) {
    return ko.isObservable(instance)
        && typeof instance["remove"] == "function"
        && typeof instance["push"] == "function";
};

ko.exportSymbol('observableArray', ko.observableArray);
ko.exportSymbol('isObservableArray', ko.isObservableArray);
var arrayChangeEventName = 'arrayChange';
ko.extenders['trackArrayChanges'] = function(target, options) {
    // Use the provided options--each call to trackArrayChanges overwrites the previously set options
    target.compareArrayOptions = {};
    if (options && typeof options == "object") {
        ko.utils.extend(target.compareArrayOptions, options);
    }
    target.compareArrayOptions['sparse'] = true;

    // Only modify the target observable once
    if (target.cacheDiffForKnownOperation) {
        return;
    }
    var trackingChanges = false,
        cachedDiff = null,
        changeSubscription,
        spectateSubscription,
        pendingChanges = 0,
        previousContents,
        underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,
        underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;

    // Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
    target.beforeSubscriptionAdd = function (event) {
        if (underlyingBeforeSubscriptionAddFunction) {
            underlyingBeforeSubscriptionAddFunction.call(target, event);
        }
        if (event === arrayChangeEventName) {
            trackChanges();
        }
    };
    // Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
    target.afterSubscriptionRemove = function (event) {
        if (underlyingAfterSubscriptionRemoveFunction) {
            underlyingAfterSubscriptionRemoveFunction.call(target, event);
        }
        if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
            if (changeSubscription) {
                changeSubscription.dispose();
            }
            if (spectateSubscription) {
                spectateSubscription.dispose();
            }
            spectateSubscription = changeSubscription = null;
            trackingChanges = false;
            previousContents = undefined;
        }
    };

    function trackChanges() {
        if (trackingChanges) {
            // Whenever there's a new subscription and there are pending notifications, make sure all previous
            // subscriptions are notified of the change so that all subscriptions are in sync.
            notifyChanges();
            return;
        }

        trackingChanges = true;

        // Track how many times the array actually changed value
        spectateSubscription = target.subscribe(function () {
            ++pendingChanges;
        }, null, "spectate");

        // Each time the array changes value, capture a clone so that on the next
        // change it's possible to produce a diff
        previousContents = [].concat(target.peek() || []);
        cachedDiff = null;
        changeSubscription = target.subscribe(notifyChanges);

        function notifyChanges() {
            if (pendingChanges) {
                // Make a copy of the current contents and ensure it's an array
                var currentContents = [].concat(target.peek() || []), changes;

                // Compute the diff and issue notifications, but only if someone is listening
                if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
                    changes = getChanges(previousContents, currentContents);
                }

                // Eliminate references to the old, removed items, so they can be GCed
                previousContents = currentContents;
                cachedDiff = null;
                pendingChanges = 0;

                if (changes && changes.length) {
                    target['notifySubscribers'](changes, arrayChangeEventName);
                }
            }
        }
    }

    function getChanges(previousContents, currentContents) {
        // We try to re-use cached diffs.
        // The scenarios where pendingChanges > 1 are when using rate limiting or deferred updates,
        // which without this check would not be compatible with arrayChange notifications. Normally,
        // notifications are issued immediately so we wouldn't be queueing up more than one.
        if (!cachedDiff || pendingChanges > 1) {
            cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions);
        }

        return cachedDiff;
    }

    target.cacheDiffForKnownOperation = function(rawArray, operationName, args) {
        // Only run if we're currently tracking changes for this observable array
        // and there aren't any pending deferred notifications.
        if (!trackingChanges || pendingChanges) {
            return;
        }
        var diff = [],
            arrayLength = rawArray.length,
            argsLength = args.length,
            offset = 0;

        function pushDiff(status, value, index) {
            return diff[diff.length] = { 'status': status, 'value': value, 'index': index };
        }
        switch (operationName) {
            case 'push':
                offset = arrayLength;
            case 'unshift':
                for (var index = 0; index < argsLength; index++) {
                    pushDiff('added', args[index], offset + index);
                }
                break;

            case 'pop':
                offset = arrayLength - 1;
            case 'shift':
                if (arrayLength) {
                    pushDiff('deleted', rawArray[offset], offset);
                }
                break;

            case 'splice':
                // Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].
                // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
                var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength),
                    endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength),
                    endAddIndex = startIndex + argsLength - 2,
                    endIndex = Math.max(endDeleteIndex, endAddIndex),
                    additions = [], deletions = [];
                for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
                    if (index < endDeleteIndex)
                        deletions.push(pushDiff('deleted', rawArray[index], index));
                    if (index < endAddIndex)
                        additions.push(pushDiff('added', args[argsIndex], index));
                }
                ko.utils.findMovesInArrayComparison(deletions, additions);
                break;

            default:
                return;
        }
        cachedDiff = diff;
    };
};
var computedState = ko.utils.createSymbolOrString('_state');

ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
    if (typeof evaluatorFunctionOrOptions === "object") {
        // Single-parameter syntax - everything is on this "options" param
        options = evaluatorFunctionOrOptions;
    } else {
        // Multi-parameter syntax - construct the options according to the params passed
        options = options || {};
        if (evaluatorFunctionOrOptions) {
            options["read"] = evaluatorFunctionOrOptions;
        }
    }
    if (typeof options["read"] != "function")
        throw Error("Pass a function that returns the value of the ko.computed");

    var writeFunction = options["write"];
    var state = {
        latestValue: undefined,
        isStale: true,
        isDirty: true,
        isBeingEvaluated: false,
        suppressDisposalUntilDisposeWhenReturnsFalse: false,
        isDisposed: false,
        pure: false,
        isSleeping: false,
        readFunction: options["read"],
        evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
        disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
        disposeWhen: options["disposeWhen"] || options.disposeWhen,
        domNodeDisposalCallback: null,
        dependencyTracking: {},
        dependenciesCount: 0,
        evaluationTimeoutInstance: null
    };

    function computedObservable() {
        if (arguments.length > 0) {
            if (typeof writeFunction === "function") {
                // Writing a value
                writeFunction.apply(state.evaluatorFunctionTarget, arguments);
            } else {
                throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
            }
            return this; // Permits chained assignments
        } else {
            // Reading the value
            if (!state.isDisposed) {
                ko.dependencyDetection.registerDependency(computedObservable);
            }
            if (state.isDirty || (state.isSleeping && computedObservable.haveDependenciesChanged())) {
                computedObservable.evaluateImmediate();
            }
            return state.latestValue;
        }
    }

    computedObservable[computedState] = state;
    computedObservable.hasWriteFunction = typeof writeFunction === "function";

    // Inherit from 'subscribable'
    if (!ko.utils.canSetPrototype) {
        // 'subscribable' won't be on the prototype chain unless we put it there directly
        ko.utils.extend(computedObservable, ko.subscribable['fn']);
    }
    ko.subscribable['fn'].init(computedObservable);

    // Inherit from 'computed'
    ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);

    if (options['pure']) {
        state.pure = true;
        state.isSleeping = true;     // Starts off sleeping; will awake on the first subscription
        ko.utils.extend(computedObservable, pureComputedOverrides);
    } else if (options['deferEvaluation']) {
        ko.utils.extend(computedObservable, deferEvaluationOverrides);
    }

    if (ko.options['deferUpdates']) {
        ko.extenders['deferred'](computedObservable, true);
    }

    if (DEBUG) {
        // #1731 - Aid debugging by exposing the computed's options
        computedObservable["_options"] = options;
    }

    if (state.disposeWhenNodeIsRemoved) {
        // Since this computed is associated with a DOM node, and we don't want to dispose the computed
        // until the DOM node is *removed* from the document (as opposed to never having been in the document),
        // we'll prevent disposal until "disposeWhen" first returns false.
        state.suppressDisposalUntilDisposeWhenReturnsFalse = true;

        // disposeWhenNodeIsRemoved: true can be used to opt into the "only dispose after first false result"
        // behaviour even if there's no specific node to watch. In that case, clear the option so we don't try
        // to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't
        // be documented or used by application code, as it's likely to change in a future version of KO.
        if (!state.disposeWhenNodeIsRemoved.nodeType) {
            state.disposeWhenNodeIsRemoved = null;
        }
    }

    // Evaluate, unless sleeping or deferEvaluation is true
    if (!state.isSleeping && !options['deferEvaluation']) {
        computedObservable.evaluateImmediate();
    }

    // Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
    // removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
    if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {
        ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {
            computedObservable.dispose();
        });
    }

    return computedObservable;
};

// Utility function that disposes a given dependencyTracking entry
function computedDisposeDependencyCallback(id, entryToDispose) {
    if (entryToDispose !== null && entryToDispose.dispose) {
        entryToDispose.dispose();
    }
}

// This function gets called each time a dependency is detected while evaluating a computed.
// It's factored out as a shared function to avoid creating unnecessary function instances during evaluation.
function computedBeginDependencyDetectionCallback(subscribable, id) {
    var computedObservable = this.computedObservable,
        state = computedObservable[computedState];
    if (!state.isDisposed) {
        if (this.disposalCount && this.disposalCandidates[id]) {
            // Don't want to dispose this subscription, as it's still being used
            computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]);
            this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway
            --this.disposalCount;
        } else if (!state.dependencyTracking[id]) {
            // Brand new subscription - add it
            computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable));
        }
        // If the observable we've accessed has a pending notification, ensure we get notified of the actual final value (bypass equality checks)
        if (subscribable._notificationIsPending) {
            subscribable._notifyNextChangeIfValueIsDifferent();
        }
    }
}

var computedFn = {
    "equalityComparer": valuesArePrimitiveAndEqual,
    getDependenciesCount: function () {
        return this[computedState].dependenciesCount;
    },
    getDependencies: function () {
        var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];

        ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
            dependentObservables[dependency._order] = dependency._target;
        });

        return dependentObservables;
    },
    hasAncestorDependency: function (obs) {
        if (!this[computedState].dependenciesCount) {
            return false;
        }
        var dependencies = this.getDependencies();
        if (ko.utils.arrayIndexOf(dependencies, obs) !== -1) {
            return true;
        }
        return !!ko.utils.arrayFirst(dependencies, function (dep) {
            return dep.hasAncestorDependency && dep.hasAncestorDependency(obs);
        });
    },
    addDependencyTracking: function (id, target, trackingObj) {
        if (this[computedState].pure && target === this) {
            throw Error("A 'pure' computed must not be called recursively");
        }

        this[computedState].dependencyTracking[id] = trackingObj;
        trackingObj._order = this[computedState].dependenciesCount++;
        trackingObj._version = target.getVersion();
    },
    haveDependenciesChanged: function () {
        var id, dependency, dependencyTracking = this[computedState].dependencyTracking;
        for (id in dependencyTracking) {
            if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) {
                dependency = dependencyTracking[id];
                if ((this._evalDelayed && dependency._target._notificationIsPending) || dependency._target.hasChanged(dependency._version)) {
                    return true;
                }
            }
        }
    },
    markDirty: function () {
        // Process "dirty" events if we can handle delayed notifications
        if (this._evalDelayed && !this[computedState].isBeingEvaluated) {
            this._evalDelayed(false /*isChange*/);
        }
    },
    isActive: function () {
        var state = this[computedState];
        return state.isDirty || state.dependenciesCount > 0;
    },
    respondToChange: function () {
        // Ignore "change" events if we've already scheduled a delayed notification
        if (!this._notificationIsPending) {
            this.evaluatePossiblyAsync();
        } else if (this[computedState].isDirty) {
            this[computedState].isStale = true;
        }
    },
    subscribeToDependency: function (target) {
        if (target._deferUpdates) {
            var dirtySub = target.subscribe(this.markDirty, this, 'dirty'),
                changeSub = target.subscribe(this.respondToChange, this);
            return {
                _target: target,
                dispose: function () {
                    dirtySub.dispose();
                    changeSub.dispose();
                }
            };
        } else {
            return target.subscribe(this.evaluatePossiblyAsync, this);
        }
    },
    evaluatePossiblyAsync: function () {
        var computedObservable = this,
            throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
        if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
            clearTimeout(this[computedState].evaluationTimeoutInstance);
            this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {
                computedObservable.evaluateImmediate(true /*notifyChange*/);
            }, throttleEvaluationTimeout);
        } else if (computedObservable._evalDelayed) {
            computedObservable._evalDelayed(true /*isChange*/);
        } else {
            computedObservable.evaluateImmediate(true /*notifyChange*/);
        }
    },
    evaluateImmediate: function (notifyChange) {
        var computedObservable = this,
            state = computedObservable[computedState],
            disposeWhen = state.disposeWhen,
            changed = false;

        if (state.isBeingEvaluated) {
            // If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
            // This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
            // certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
            // their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
            return;
        }

        // Do not evaluate (and possibly capture new dependencies) if disposed
        if (state.isDisposed) {
            return;
        }

        if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) {
            // See comment above about suppressDisposalUntilDisposeWhenReturnsFalse
            if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) {
                computedObservable.dispose();
                return;
            }
        } else {
            // It just did return false, so we can stop suppressing now
            state.suppressDisposalUntilDisposeWhenReturnsFalse = false;
        }

        state.isBeingEvaluated = true;
        try {
            changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);
        } finally {
            state.isBeingEvaluated = false;
        }

        return changed;
    },
    evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {
        // This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else.
        // Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate,
        // which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least).

        var computedObservable = this,
            state = computedObservable[computedState],
            changed = false;

        // Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
        // Then, during evaluation, we cross off any that are in fact still being used.
        var isInitial = state.pure ? undefined : !state.dependenciesCount,   // If we're evaluating when there are no previous dependencies, it must be the first time
            dependencyDetectionContext = {
                computedObservable: computedObservable,
                disposalCandidates: state.dependencyTracking,
                disposalCount: state.dependenciesCount
            };

        ko.dependencyDetection.begin({
            callbackTarget: dependencyDetectionContext,
            callback: computedBeginDependencyDetectionCallback,
            computed: computedObservable,
            isInitial: isInitial
        });

        state.dependencyTracking = {};
        state.dependenciesCount = 0;

        var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext);

        if (!state.dependenciesCount) {
            computedObservable.dispose();
            changed = true; // When evaluation causes a disposal, make sure all dependent computeds get notified so they'll see the new state
        } else {
            changed = computedObservable.isDifferent(state.latestValue, newValue);
        }

        if (changed) {
            if (!state.isSleeping) {
                computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
            } else {
                computedObservable.updateVersion();
            }

            state.latestValue = newValue;
            if (DEBUG) computedObservable._latestValue = newValue;

            computedObservable["notifySubscribers"](state.latestValue, "spectate");

            if (!state.isSleeping && notifyChange) {
                computedObservable["notifySubscribers"](state.latestValue);
            }
            if (computedObservable._recordUpdate) {
                computedObservable._recordUpdate();
            }
        }

        if (isInitial) {
            computedObservable["notifySubscribers"](state.latestValue, "awake");
        }

        return changed;
    },
    evaluateImmediate_CallReadThenEndDependencyDetection: function (state, dependencyDetectionContext) {
        // This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic.
        // You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection
        // can be independent of try/finally blocks, which contributes to saving about 40% off the CPU
        // overhead of computed evaluation (on V8 at least).

        try {
            var readFunction = state.readFunction;
            return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
        } finally {
            ko.dependencyDetection.end();

            // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
            if (dependencyDetectionContext.disposalCount && !state.isSleeping) {
                ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);
            }

            state.isStale = state.isDirty = false;
        }
    },
    peek: function (evaluate) {
        // By default, peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set.
        // Pass in true to evaluate if needed.
        var state = this[computedState];
        if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) {
            this.evaluateImmediate();
        }
        return state.latestValue;
    },
    limit: function (limitFunction) {
        // Override the limit function with one that delays evaluation as well
        ko.subscribable['fn'].limit.call(this, limitFunction);
        this._evalIfChanged = function () {
            if (!this[computedState].isSleeping) {
                if (this[computedState].isStale) {
                    this.evaluateImmediate();
                } else {
                    this[computedState].isDirty = false;
                }
            }
            return this[computedState].latestValue;
        };
        this._evalDelayed = function (isChange) {
            this._limitBeforeChange(this[computedState].latestValue);

            // Mark as dirty
            this[computedState].isDirty = true;
            if (isChange) {
                this[computedState].isStale = true;
            }

            // Pass the observable to the "limit" code, which will evaluate it when
            // it's time to do the notification.
            this._limitChange(this, !isChange /* isDirty */);
        };
    },
    dispose: function () {
        var state = this[computedState];
        if (!state.isSleeping && state.dependencyTracking) {
            ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
                if (dependency.dispose)
                    dependency.dispose();
            });
        }
        if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {
            ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);
        }
        state.dependencyTracking = undefined;
        state.dependenciesCount = 0;
        state.isDisposed = true;
        state.isStale = false;
        state.isDirty = false;
        state.isSleeping = false;
        state.disposeWhenNodeIsRemoved = undefined;
        state.disposeWhen = undefined;
        state.readFunction = undefined;
        if (!this.hasWriteFunction) {
            state.evaluatorFunctionTarget = undefined;
        }
    }
};

var pureComputedOverrides = {
    beforeSubscriptionAdd: function (event) {
        // If asleep, wake up the computed by subscribing to any dependencies.
        var computedObservable = this,
            state = computedObservable[computedState];
        if (!state.isDisposed && state.isSleeping && event == 'change') {
            state.isSleeping = false;
            if (state.isStale || computedObservable.haveDependenciesChanged()) {
                state.dependencyTracking = null;
                state.dependenciesCount = 0;
                if (computedObservable.evaluateImmediate()) {
                    computedObservable.updateVersion();
                }
            } else {
                // First put the dependencies in order
                var dependenciesOrder = [];
                ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
                    dependenciesOrder[dependency._order] = id;
                });
                // Next, subscribe to each one
                ko.utils.arrayForEach(dependenciesOrder, function (id, order) {
                    var dependency = state.dependencyTracking[id],
                        subscription = computedObservable.subscribeToDependency(dependency._target);
                    subscription._order = order;
                    subscription._version = dependency._version;
                    state.dependencyTracking[id] = subscription;
                });
                // Waking dependencies may have triggered effects
                if (computedObservable.haveDependenciesChanged()) {
                    if (computedObservable.evaluateImmediate()) {
                        computedObservable.updateVersion();
                    }
                }
            }

            if (!state.isDisposed) {     // test since evaluating could trigger disposal
                computedObservable["notifySubscribers"](state.latestValue, "awake");
            }
        }
    },
    afterSubscriptionRemove: function (event) {
        var state = this[computedState];
        if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
            ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
                if (dependency.dispose) {
                    state.dependencyTracking[id] = {
                        _target: dependency._target,
                        _order: dependency._order,
                        _version: dependency._version
                    };
                    dependency.dispose();
                }
            });
            state.isSleeping = true;
            this["notifySubscribers"](undefined, "asleep");
        }
    },
    getVersion: function () {
        // Because a pure computed is not automatically updated while it is sleeping, we can't
        // simply return the version number. Instead, we check if any of the dependencies have
        // changed and conditionally re-evaluate the computed observable.
        var state = this[computedState];
        if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) {
            this.evaluateImmediate();
        }
        return ko.subscribable['fn'].getVersion.call(this);
    }
};

var deferEvaluationOverrides = {
    beforeSubscriptionAdd: function (event) {
        // This will force a computed with deferEvaluation to evaluate when the first subscription is registered.
        if (event == 'change' || event == 'beforeChange') {
            this.peek();
        }
    }
};

// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.computed constructor
if (ko.utils.canSetPrototype) {
    ko.utils.setPrototypeOf(computedFn, ko.subscribable['fn']);
}

// Set the proto values for ko.computed
var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
computedFn[protoProp] = ko.computed;

ko.isComputed = function (instance) {
    return (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);
};

ko.isPureComputed = function (instance) {
    return ko.isComputed(instance) && instance[computedState] && instance[computedState].pure;
};

ko.exportSymbol('computed', ko.computed);
ko.exportSymbol('dependentObservable', ko.computed);    // export ko.dependentObservable for backwards compatibility (1.x)
ko.exportSymbol('isComputed', ko.isComputed);
ko.exportSymbol('isPureComputed', ko.isPureComputed);
ko.exportSymbol('computed.fn', computedFn);
ko.exportProperty(computedFn, 'peek', computedFn.peek);
ko.exportProperty(computedFn, 'dispose', computedFn.dispose);
ko.exportProperty(computedFn, 'isActive', computedFn.isActive);
ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);
ko.exportProperty(computedFn, 'getDependencies', computedFn.getDependencies);

ko.pureComputed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget) {
    if (typeof evaluatorFunctionOrOptions === 'function') {
        return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, {'pure':true});
    } else {
        evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions);   // make a copy of the parameter object
        evaluatorFunctionOrOptions['pure'] = true;
        return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);
    }
}
ko.exportSymbol('pureComputed', ko.pureComputed);

(function() {
    var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)

    ko.toJS = function(rootObject) {
        if (arguments.length == 0)
            throw new Error("When calling ko.toJS, pass the object you want to convert.");

        // We just unwrap everything at every level in the object graph
        return mapJsObjectGraph(rootObject, function(valueToMap) {
            // Loop because an observable's value might in turn be another observable wrapper
            for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
                valueToMap = valueToMap();
            return valueToMap;
        });
    };

    ko.toJSON = function(rootObject, replacer, space) {     // replacer and space are optional
        var plainJavaScriptObject = ko.toJS(rootObject);
        return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
    };

    function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
        visitedObjects = visitedObjects || new objectLookup();

        rootObject = mapInputCallback(rootObject);
        var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
        if (!canHaveProperties)
            return rootObject;

        var outputProperties = rootObject instanceof Array ? [] : {};
        visitedObjects.save(rootObject, outputProperties);

        visitPropertiesOrArrayEntries(rootObject, function(indexer) {
            var propertyValue = mapInputCallback(rootObject[indexer]);

            switch (typeof propertyValue) {
                case "boolean":
                case "number":
                case "string":
                case "function":
                    outputProperties[indexer] = propertyValue;
                    break;
                case "object":
                case "undefined":
                    var previouslyMappedValue = visitedObjects.get(propertyValue);
                    outputProperties[indexer] = (previouslyMappedValue !== undefined)
                        ? previouslyMappedValue
                        : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
                    break;
            }
        });

        return outputProperties;
    }

    function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
        if (rootObject instanceof Array) {
            for (var i = 0; i < rootObject.length; i++)
                visitorCallback(i);

            // For arrays, also respect toJSON property for custom mappings (fixes #278)
            if (typeof rootObject['toJSON'] == 'function')
                visitorCallback('toJSON');
        } else {
            for (var propertyName in rootObject) {
                visitorCallback(propertyName);
            }
        }
    };

    function objectLookup() {
        this.keys = [];
        this.values = [];
    };

    objectLookup.prototype = {
        constructor: objectLookup,
        save: function(key, value) {
            var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
            if (existingIndex >= 0)
                this.values[existingIndex] = value;
            else {
                this.keys.push(key);
                this.values.push(value);
            }
        },
        get: function(key) {
            var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
            return (existingIndex >= 0) ? this.values[existingIndex] : undefined;
        }
    };
})();

ko.exportSymbol('toJS', ko.toJS);
ko.exportSymbol('toJSON', ko.toJSON);
ko.when = function(predicate, callback, context) {
    function kowhen (resolve) {
        var observable = ko.pureComputed(predicate, context).extend({notify:'always'});
        var subscription = observable.subscribe(function(value) {
            if (value) {
                subscription.dispose();
                resolve(value);
            }
        });
        // In case the initial value is true, process it right away
        observable['notifySubscribers'](observable.peek());

        return subscription;
    }
    if (typeof Promise === "function" && !callback) {
        return new Promise(kowhen);
    } else {
        return kowhen(callback.bind(context));
    }
};

ko.exportSymbol('when', ko.when);
(function () {
    var hasDomDataExpandoProperty = '__ko__hasDomDataOptionValue__';

    // Normally, SELECT elements and their OPTIONs can only take value of type 'string' (because the values
    // are stored on DOM attributes). ko.selectExtensions provides a way for SELECTs/OPTIONs to have values
    // that are arbitrary objects. This is very convenient when implementing things like cascading dropdowns.
    ko.selectExtensions = {
        readValue : function(element) {
            switch (ko.utils.tagNameLower(element)) {
                case 'option':
                    if (element[hasDomDataExpandoProperty] === true)
                        return ko.utils.domData.get(element, ko.bindingHandlers.options.optionValueDomDataKey);
                    return ko.utils.ieVersion <= 7
                        ? (element.getAttributeNode('value') && element.getAttributeNode('value').specified ? element.value : element.text)
                        : element.value;
                case 'select':
                    return element.selectedIndex >= 0 ? ko.selectExtensions.readValue(element.options[element.selectedIndex]) : undefined;
                default:
                    return element.value;
            }
        },

        writeValue: function(element, value, allowUnset) {
            switch (ko.utils.tagNameLower(element)) {
                case 'option':
                    if (typeof value === "string") {
                        ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, undefined);
                        if (hasDomDataExpandoProperty in element) { // IE <= 8 throws errors if you delete non-existent properties from a DOM node
                            delete element[hasDomDataExpandoProperty];
                        }
                        element.value = value;
                    }
                    else {
                        // Store arbitrary object using DomData
                        ko.utils.domData.set(element, ko.bindingHandlers.options.optionValueDomDataKey, value);
                        element[hasDomDataExpandoProperty] = true;

                        // Special treatment of numbers is just for backward compatibility. KO 1.2.1 wrote numerical values to element.value.
                        element.value = typeof value === "number" ? value : "";
                    }
                    break;
                case 'select':
                    if (value === "" || value === null)       // A blank string or null value will select the caption
                        value = undefined;
                    var selection = -1;
                    for (var i = 0, n = element.options.length, optionValue; i < n; ++i) {
                        optionValue = ko.selectExtensions.readValue(element.options[i]);
                        // Include special check to handle selecting a caption with a blank string value
                        if (optionValue == value || (optionValue === "" && value === undefined)) {
                            selection = i;
                            break;
                        }
                    }
                    if (allowUnset || selection >= 0 || (value === undefined && element.size > 1)) {
                        element.selectedIndex = selection;
                        if (ko.utils.ieVersion === 6) {
                            // Workaround for IE6 bug: It won't reliably apply values to SELECT nodes during the same execution thread
                            // right after you've changed the set of OPTION nodes on it. So for that node type, we'll schedule a second thread
                            // to apply the value as well.
                            ko.utils.setTimeout(function () {
                                element.selectedIndex = selection;
                            }, 0);
                        }
                    }
                    break;
                default:
                    if ((value === null) || (value === undefined))
                        value = "";
                    element.value = value;
                    break;
            }
        }
    };
})();

ko.exportSymbol('selectExtensions', ko.selectExtensions);
ko.exportSymbol('selectExtensions.readValue', ko.selectExtensions.readValue);
ko.exportSymbol('selectExtensions.writeValue', ko.selectExtensions.writeValue);
ko.expressionRewriting = (function () {
    var javaScriptReservedWords = ["true", "false", "null", "undefined"];

    // Matches something that can be assigned to--either an isolated identifier or something ending with a property accessor
    // This is designed to be simple and avoid false negatives, but could produce false positives (e.g., a+b.c).
    // This also will not properly handle nested brackets (e.g., obj1[obj2['prop']]; see #911).
    var javaScriptAssignmentTarget = /^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i;

    function getWriteableValue(expression) {
        if (ko.utils.arrayIndexOf(javaScriptReservedWords, expression) >= 0)
            return false;
        var match = expression.match(javaScriptAssignmentTarget);
        return match === null ? false : match[1] ? ('Object(' + match[1] + ')' + match[2]) : expression;
    }

    // The following regular expressions will be used to split an object-literal string into tokens

    var specials = ',"\'`{}()/:[\\]',    // These characters have special meaning to the parser and must not appear in the middle of a token, except as part of a string.
        // Create the actual regular expression by or-ing the following regex strings. The order is important.
        bindingToken = RegExp([
            // These match strings, either with double quotes, single quotes, or backticks
            '"(?:\\\\.|[^"])*"',
            "'(?:\\\\.|[^'])*'",
            "`(?:\\\\.|[^`])*`",
            // Match C style comments
            "/\\*(?:[^*]|\\*+[^*/])*\\*+/",
            // Match C++ style comments
            "//.*\n",
            // Match a regular expression (text enclosed by slashes), but will also match sets of divisions
            // as a regular expression (this is handled by the parsing loop below).
            '/(?:\\\\.|[^/])+/\w*',
            // Match text (at least two characters) that does not contain any of the above special characters,
            // although some of the special characters are allowed to start it (all but the colon and comma).
            // The text can contain spaces, but leading or trailing spaces are skipped.
            '[^\\s:,/][^' + specials + ']*[^\\s' + specials + ']',
            // Match any non-space character not matched already. This will match colons and commas, since they're
            // not matched by "everyThingElse", but will also match any other single character that wasn't already
            // matched (for example: in "a: 1, b: 2", each of the non-space characters will be matched by oneNotSpace).
            '[^\\s]'
        ].join('|'), 'g'),

        // Match end of previous token to determine whether a slash is a division or regex.
        divisionLookBehind = /[\])"'A-Za-z0-9_$]+$/,
        keywordRegexLookBehind = {'in':1,'return':1,'typeof':1};

    function parseObjectLiteral(objectLiteralString) {
        // Trim leading and trailing spaces from the string
        var str = ko.utils.stringTrim(objectLiteralString);

        // Trim braces '{' surrounding the whole object literal
        if (str.charCodeAt(0) === 123) str = str.slice(1, -1);

        // Add a newline to correctly match a C++ style comment at the end of the string and
        // add a comma so that we don't need a separate code block to deal with the last item
        str += "\n,";

        // Split into tokens
        var result = [], toks = str.match(bindingToken), key, values = [], depth = 0;

        if (toks.length > 1) {
            for (var i = 0, tok; tok = toks[i]; ++i) {
                var c = tok.charCodeAt(0);
                // A comma signals the end of a key/value pair if depth is zero
                if (c === 44) { // ","
                    if (depth <= 0) {
                        result.push((key && values.length) ? {key: key, value: values.join('')} : {'unknown': key || values.join('')});
                        key = depth = 0;
                        values = [];
                        continue;
                    }
                // Simply skip the colon that separates the name and value
                } else if (c === 58) { // ":"
                    if (!depth && !key && values.length === 1) {
                        key = values.pop();
                        continue;
                    }
                // Comments: skip them
                } else if (c === 47 && tok.length > 1 && (tok.charCodeAt(1) === 47 || tok.charCodeAt(1) === 42)) {  // "//" or "/*"
                    continue;
                // A set of slashes is initially matched as a regular expression, but could be division
                } else if (c === 47 && i && tok.length > 1) {  // "/"
                    // Look at the end of the previous token to determine if the slash is actually division
                    var match = toks[i-1].match(divisionLookBehind);
                    if (match && !keywordRegexLookBehind[match[0]]) {
                        // The slash is actually a division punctuator; re-parse the remainder of the string (not including the slash)
                        str = str.substr(str.indexOf(tok) + 1);
                        toks = str.match(bindingToken);
                        i = -1;
                        // Continue with just the slash
                        tok = '/';
                    }
                // Increment depth for parentheses, braces, and brackets so that interior commas are ignored
                } else if (c === 40 || c === 123 || c === 91) { // '(', '{', '['
                    ++depth;
                } else if (c === 41 || c === 125 || c === 93) { // ')', '}', ']'
                    --depth;
                // The key will be the first token; if it's a string, trim the quotes
                } else if (!key && !values.length && (c === 34 || c === 39)) { // '"', "'"
                    tok = tok.slice(1, -1);
                }
                values.push(tok);
            }
            if (depth > 0) {
                throw Error("Unbalanced parentheses, braces, or brackets");
            }
        }
        return result;
    }

    // Two-way bindings include a write function that allow the handler to update the value even if it's not an observable.
    var twoWayBindings = {};

    function preProcessBindings(bindingsStringOrKeyValueArray, bindingOptions) {
        bindingOptions = bindingOptions || {};

        function processKeyValue(key, val) {
            var writableVal;
            function callPreprocessHook(obj) {
                return (obj && obj['preprocess']) ? (val = obj['preprocess'](val, key, processKeyValue)) : true;
            }
            if (!bindingParams) {
                if (!callPreprocessHook(ko['getBindingHandler'](key)))
                    return;

                if (twoWayBindings[key] && (writableVal = getWriteableValue(val))) {
                    // For two-way bindings, provide a write method in case the value
                    // isn't a writable observable.
                    var writeKey = typeof twoWayBindings[key] == 'string' ? twoWayBindings[key] : key;
                    propertyAccessorResultStrings.push("'" + writeKey + "':function(_z){" + writableVal + "=_z}");
                }
            }
            // Values are wrapped in a function so that each value can be accessed independently
            if (makeValueAccessors) {
                val = 'function(){return ' + val + ' }';
            }
            resultStrings.push("'" + key + "':" + val);
        }

        var resultStrings = [],
            propertyAccessorResultStrings = [],
            makeValueAccessors = bindingOptions['valueAccessors'],
            bindingParams = bindingOptions['bindingParams'],
            keyValueArray = typeof bindingsStringOrKeyValueArray === "string" ?
                parseObjectLiteral(bindingsStringOrKeyValueArray) : bindingsStringOrKeyValueArray;

        ko.utils.arrayForEach(keyValueArray, function(keyValue) {
            processKeyValue(keyValue.key || keyValue['unknown'], keyValue.value);
        });

        if (propertyAccessorResultStrings.length)
            processKeyValue('_ko_property_writers', "{" + propertyAccessorResultStrings.join(",") + " }");

        return resultStrings.join(",");
    }

    return {
        bindingRewriteValidators: [],

        twoWayBindings: twoWayBindings,

        parseObjectLiteral: parseObjectLiteral,

        preProcessBindings: preProcessBindings,

        keyValueArrayContainsKey: function(keyValueArray, key) {
            for (var i = 0; i < keyValueArray.length; i++)
                if (keyValueArray[i]['key'] == key)
                    return true;
            return false;
        },

        // Internal, private KO utility for updating model properties from within bindings
        // property:            If the property being updated is (or might be) an observable, pass it here
        //                      If it turns out to be a writable observable, it will be written to directly
        // allBindings:         An object with a get method to retrieve bindings in the current execution context.
        //                      This will be searched for a '_ko_property_writers' property in case you're writing to a non-observable
        // key:                 The key identifying the property to be written. Example: for { hasFocus: myValue }, write to 'myValue' by specifying the key 'hasFocus'
        // value:               The value to be written
        // checkIfDifferent:    If true, and if the property being written is a writable observable, the value will only be written if
        //                      it is !== existing value on that writable observable
        writeValueToProperty: function(property, allBindings, key, value, checkIfDifferent) {
            if (!property || !ko.isObservable(property)) {
                var propWriters = allBindings.get('_ko_property_writers');
                if (propWriters && propWriters[key])
                    propWriters[key](value);
            } else if (ko.isWriteableObservable(property) && (!checkIfDifferent || property.peek() !== value)) {
                property(value);
            }
        }
    };
})();

ko.exportSymbol('expressionRewriting', ko.expressionRewriting);
ko.exportSymbol('expressionRewriting.bindingRewriteValidators', ko.expressionRewriting.bindingRewriteValidators);
ko.exportSymbol('expressionRewriting.parseObjectLiteral', ko.expressionRewriting.parseObjectLiteral);
ko.exportSymbol('expressionRewriting.preProcessBindings', ko.expressionRewriting.preProcessBindings);

// Making bindings explicitly declare themselves as "two way" isn't ideal in the long term (it would be better if
// all bindings could use an official 'property writer' API without needing to declare that they might). However,
// since this is not, and has never been, a public API (_ko_property_writers was never documented), it's acceptable
// as an internal implementation detail in the short term.
// For those developers who rely on _ko_property_writers in their custom bindings, we expose _twoWayBindings as an
// undocumented feature that makes it relatively easy to upgrade to KO 3.0. However, this is still not an official
// public API, and we reserve the right to remove it at any time if we create a real public property writers API.
ko.exportSymbol('expressionRewriting._twoWayBindings', ko.expressionRewriting.twoWayBindings);

// For backward compatibility, define the following aliases. (Previously, these function names were misleading because
// they referred to JSON specifically, even though they actually work with arbitrary JavaScript object literal expressions.)
ko.exportSymbol('jsonExpressionRewriting', ko.expressionRewriting);
ko.exportSymbol('jsonExpressionRewriting.insertPropertyAccessorsIntoJson', ko.expressionRewriting.preProcessBindings);
(function() {
    // "Virtual elements" is an abstraction on top of the usual DOM API which understands the notion that comment nodes
    // may be used to represent hierarchy (in addition to the DOM's natural hierarchy).
    // If you call the DOM-manipulating functions on ko.virtualElements, you will be able to read and write the state
    // of that virtual hierarchy
    //
    // The point of all this is to support containerless templates (e.g., <!-- ko foreach:someCollection -->blah<!-- /ko -->)
    // without having to scatter special cases all over the binding and templating code.

    // IE 9 cannot reliably read the "nodeValue" property of a comment node (see https://github.com/SteveSanderson/knockout/issues/186)
    // but it does give them a nonstandard alternative property called "text" that it can read reliably. Other browsers don't have that property.
    // So, use node.text where available, and node.nodeValue elsewhere
    var commentNodesHaveTextProperty = document && document.createComment("test").text === "<!--test-->";

    var startCommentRegex = commentNodesHaveTextProperty ? /^<!--\s*ko(?:\s+([\s\S]+))?\s*-->$/ : /^\s*ko(?:\s+([\s\S]+))?\s*$/;
    var endCommentRegex =   commentNodesHaveTextProperty ? /^<!--\s*\/ko\s*-->$/ : /^\s*\/ko\s*$/;
    var htmlTagsWithOptionallyClosingChildren = { 'ul': true, 'ol': true };

    function isStartComment(node) {
        return (node.nodeType == 8) && startCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
    }

    function isEndComment(node) {
        return (node.nodeType == 8) && endCommentRegex.test(commentNodesHaveTextProperty ? node.text : node.nodeValue);
    }

    function isUnmatchedEndComment(node) {
        return isEndComment(node) && !(ko.utils.domData.get(node, matchedEndCommentDataKey));
    }

    var matchedEndCommentDataKey = "__ko_matchedEndComment__"

    function getVirtualChildren(startComment, allowUnbalanced) {
        var currentNode = startComment;
        var depth = 1;
        var children = [];
        while (currentNode = currentNode.nextSibling) {
            if (isEndComment(currentNode)) {
                ko.utils.domData.set(currentNode, matchedEndCommentDataKey, true);
                depth--;
                if (depth === 0)
                    return children;
            }

            children.push(currentNode);

            if (isStartComment(currentNode))
                depth++;
        }
        if (!allowUnbalanced)
            throw new Error("Cannot find closing comment tag to match: " + startComment.nodeValue);
        return null;
    }

    function getMatchingEndComment(startComment, allowUnbalanced) {
        var allVirtualChildren = getVirtualChildren(startComment, allowUnbalanced);
        if (allVirtualChildren) {
            if (allVirtualChildren.length > 0)
                return allVirtualChildren[allVirtualChildren.length - 1].nextSibling;
            return startComment.nextSibling;
        } else
            return null; // Must have no matching end comment, and allowUnbalanced is true
    }

    function getUnbalancedChildTags(node) {
        // e.g., from <div>OK</div><!-- ko blah --><span>Another</span>, returns: <!-- ko blah --><span>Another</span>
        //       from <div>OK</div><!-- /ko --><!-- /ko -->,             returns: <!-- /ko --><!-- /ko -->
        var childNode = node.firstChild, captureRemaining = null;
        if (childNode) {
            do {
                if (captureRemaining)                   // We already hit an unbalanced node and are now just scooping up all subsequent nodes
                    captureRemaining.push(childNode);
                else if (isStartComment(childNode)) {
                    var matchingEndComment = getMatchingEndComment(childNode, /* allowUnbalanced: */ true);
                    if (matchingEndComment)             // It's a balanced tag, so skip immediately to the end of this virtual set
                        childNode = matchingEndComment;
                    else
                        captureRemaining = [childNode]; // It's unbalanced, so start capturing from this point
                } else if (isEndComment(childNode)) {
                    captureRemaining = [childNode];     // It's unbalanced (if it wasn't, we'd have skipped over it already), so start capturing
                }
            } while (childNode = childNode.nextSibling);
        }
        return captureRemaining;
    }

    ko.virtualElements = {
        allowedBindings: {},

        childNodes: function(node) {
            return isStartComment(node) ? getVirtualChildren(node) : node.childNodes;
        },

        emptyNode: function(node) {
            if (!isStartComment(node))
                ko.utils.emptyDomNode(node);
            else {
                var virtualChildren = ko.virtualElements.childNodes(node);
                for (var i = 0, j = virtualChildren.length; i < j; i++)
                    ko.removeNode(virtualChildren[i]);
            }
        },

        setDomNodeChildren: function(node, childNodes) {
            if (!isStartComment(node))
                ko.utils.setDomNodeChildren(node, childNodes);
            else {
                ko.virtualElements.emptyNode(node);
                var endCommentNode = node.nextSibling; // Must be the next sibling, as we just emptied the children
                for (var i = 0, j = childNodes.length; i < j; i++)
                    endCommentNode.parentNode.insertBefore(childNodes[i], endCommentNode);
            }
        },

        prepend: function(containerNode, nodeToPrepend) {
            var insertBeforeNode;

            if (isStartComment(containerNode)) {
                // Start comments must always have a parent and at least one following sibling (the end comment)
                insertBeforeNode = containerNode.nextSibling;
                containerNode = containerNode.parentNode;
            } else {
                insertBeforeNode = containerNode.firstChild;
            }

            if (!insertBeforeNode) {
                containerNode.appendChild(nodeToPrepend);
            } else if (nodeToPrepend !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself
                containerNode.insertBefore(nodeToPrepend, insertBeforeNode);
            }
        },

        insertAfter: function(containerNode, nodeToInsert, insertAfterNode) {
            if (!insertAfterNode) {
                ko.virtualElements.prepend(containerNode, nodeToInsert);
            } else {
                // Children of start comments must always have a parent and at least one following sibling (the end comment)
                var insertBeforeNode = insertAfterNode.nextSibling;

                if (isStartComment(containerNode)) {
                    containerNode = containerNode.parentNode;
                }

                if (!insertBeforeNode) {
                    containerNode.appendChild(nodeToInsert);
                } else if (nodeToInsert !== insertBeforeNode) {       // IE will sometimes crash if you try to insert a node before itself
                    containerNode.insertBefore(nodeToInsert, insertBeforeNode);
                }
            }
        },

        firstChild: function(node) {
            if (!isStartComment(node)) {
                if (node.firstChild && isEndComment(node.firstChild)) {
                    throw new Error("Found invalid end comment, as the first child of " + node);
                }
                return node.firstChild;
            } else if (!node.nextSibling || isEndComment(node.nextSibling)) {
                return null;
            } else {
                return node.nextSibling;
            }
        },

        nextSibling: function(node) {
            if (isStartComment(node)) {
                node = getMatchingEndComment(node);
            }

            if (node.nextSibling && isEndComment(node.nextSibling)) {
                if (isUnmatchedEndComment(node.nextSibling)) {
                    throw Error("Found end comment without a matching opening comment, as child of " + node);
                } else {
                    return null;
                }
            } else {
                return node.nextSibling;
            }
        },

        hasBindingValue: isStartComment,

        virtualNodeBindingValue: function(node) {
            var regexMatch = (commentNodesHaveTextProperty ? node.text : node.nodeValue).match(startCommentRegex);
            return regexMatch ? regexMatch[1] : null;
        },

        normaliseVirtualElementDomStructure: function(elementVerified) {
            // Workaround for https://github.com/SteveSanderson/knockout/issues/155
            // (IE <= 8 or IE 9 quirks mode parses your HTML weirdly, treating closing </li> tags as if they don't exist, thereby moving comment nodes
            // that are direct descendants of <ul> into the preceding <li>)
            if (!htmlTagsWithOptionallyClosingChildren[ko.utils.tagNameLower(elementVerified)])
                return;

            // Scan immediate children to see if they contain unbalanced comment tags. If they do, those comment tags
            // must be intended to appear *after* that child, so move them there.
            var childNode = elementVerified.firstChild;
            if (childNode) {
                do {
                    if (childNode.nodeType === 1) {
                        var unbalancedTags = getUnbalancedChildTags(childNode);
                        if (unbalancedTags) {
                            // Fix up the DOM by moving the unbalanced tags to where they most likely were intended to be placed - *after* the child
                            var nodeToInsertBefore = childNode.nextSibling;
                            for (var i = 0; i < unbalancedTags.length; i++) {
                                if (nodeToInsertBefore)
                                    elementVerified.insertBefore(unbalancedTags[i], nodeToInsertBefore);
                                else
                                    elementVerified.appendChild(unbalancedTags[i]);
                            }
                        }
                    }
                } while (childNode = childNode.nextSibling);
            }
        }
    };
})();
ko.exportSymbol('virtualElements', ko.virtualElements);
ko.exportSymbol('virtualElements.allowedBindings', ko.virtualElements.allowedBindings);
ko.exportSymbol('virtualElements.emptyNode', ko.virtualElements.emptyNode);
//ko.exportSymbol('virtualElements.firstChild', ko.virtualElements.firstChild);     // firstChild is not minified
ko.exportSymbol('virtualElements.insertAfter', ko.virtualElements.insertAfter);
//ko.exportSymbol('virtualElements.nextSibling', ko.virtualElements.nextSibling);   // nextSibling is not minified
ko.exportSymbol('virtualElements.prepend', ko.virtualElements.prepend);
ko.exportSymbol('virtualElements.setDomNodeChildren', ko.virtualElements.setDomNodeChildren);
(function() {
    var defaultBindingAttributeName = "data-bind";

    ko.bindingProvider = function() {
        this.bindingCache = {};
    };

    ko.utils.extend(ko.bindingProvider.prototype, {
        'nodeHasBindings': function(node) {
            switch (node.nodeType) {
                case 1: // Element
                    return node.getAttribute(defaultBindingAttributeName) != null
                        || ko.components['getComponentNameForNode'](node);
                case 8: // Comment node
                    return ko.virtualElements.hasBindingValue(node);
                default: return false;
            }
        },

        'getBindings': function(node, bindingContext) {
            var bindingsString = this['getBindingsString'](node, bindingContext),
                parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node) : null;
            return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ false);
        },

        'getBindingAccessors': function(node, bindingContext) {
            var bindingsString = this['getBindingsString'](node, bindingContext),
                parsedBindings = bindingsString ? this['parseBindingsString'](bindingsString, bindingContext, node, { 'valueAccessors': true }) : null;
            return ko.components.addBindingsForCustomElement(parsedBindings, node, bindingContext, /* valueAccessors */ true);
        },

        // The following function is only used internally by this default provider.
        // It's not part of the interface definition for a general binding provider.
        'getBindingsString': function(node, bindingContext) {
            switch (node.nodeType) {
                case 1: return node.getAttribute(defaultBindingAttributeName);   // Element
                case 8: return ko.virtualElements.virtualNodeBindingValue(node); // Comment node
                default: return null;
            }
        },

        // The following function is only used internally by this default provider.
        // It's not part of the interface definition for a general binding provider.
        'parseBindingsString': function(bindingsString, bindingContext, node, options) {
            try {
                var bindingFunction = createBindingsStringEvaluatorViaCache(bindingsString, this.bindingCache, options);
                return bindingFunction(bindingContext, node);
            } catch (ex) {
                ex.message = "Unable to parse bindings.\nBindings value: " + bindingsString + "\nMessage: " + ex.message;
                throw ex;
            }
        }
    });

    ko.bindingProvider['instance'] = new ko.bindingProvider();

    function createBindingsStringEvaluatorViaCache(bindingsString, cache, options) {
        var cacheKey = bindingsString + (options && options['valueAccessors'] || '');
        return cache[cacheKey]
            || (cache[cacheKey] = createBindingsStringEvaluator(bindingsString, options));
    }

    function createBindingsStringEvaluator(bindingsString, options) {
        // Build the source for a function that evaluates "expression"
        // For each scope variable, add an extra level of "with" nesting
        // Example result: with(sc1) { with(sc0) { return (expression) } }
        var rewrittenBindings = ko.expressionRewriting.preProcessBindings(bindingsString, options),
            functionBody = "with($context){with($data||{}){return{" + rewrittenBindings + "}}}";
        return new Function("$context", "$element", functionBody);
    }
})();

ko.exportSymbol('bindingProvider', ko.bindingProvider);
(function () {
    // Hide or don't minify context properties, see https://github.com/knockout/knockout/issues/2294
    var contextSubscribable = ko.utils.createSymbolOrString('_subscribable');
    var contextAncestorBindingInfo = ko.utils.createSymbolOrString('_ancestorBindingInfo');
    var contextDataDependency = ko.utils.createSymbolOrString('_dataDependency');

    ko.bindingHandlers = {};

    // The following element types will not be recursed into during binding.
    var bindingDoesNotRecurseIntoElementTypes = {
        // Don't want bindings that operate on text nodes to mutate <script> and <textarea> contents,
        // because it's unexpected and a potential XSS issue.
        // Also bindings should not operate on <template> elements since this breaks in Internet Explorer
        // and because such elements' contents are always intended to be bound in a different context
        // from where they appear in the document.
        'script': true,
        'textarea': true,
        'template': true
    };

    // Use an overridable method for retrieving binding handlers so that plugins may support dynamically created handlers
    ko['getBindingHandler'] = function(bindingKey) {
        return ko.bindingHandlers[bindingKey];
    };

    var inheritParentVm = {};

    // The ko.bindingContext constructor is only called directly to create the root context. For child
    // contexts, use bindingContext.createChildContext or bindingContext.extend.
    ko.bindingContext = function(dataItemOrAccessor, parentContext, dataItemAlias, extendCallback, options) {

        // The binding context object includes static properties for the current, parent, and root view models.
        // If a view model is actually stored in an observable, the corresponding binding context object, and
        // any child contexts, must be updated when the view model is changed.
        function updateContext() {
            // Most of the time, the context will directly get a view model object, but if a function is given,
            // we call the function to retrieve the view model. If the function accesses any observables or returns
            // an observable, the dependency is tracked, and those observables can later cause the binding
            // context to be updated.
            var dataItemOrObservable = isFunc ? realDataItemOrAccessor() : realDataItemOrAccessor,
                dataItem = ko.utils.unwrapObservable(dataItemOrObservable);

            if (parentContext) {
                // Copy $root and any custom properties from the parent context
                ko.utils.extend(self, parentContext);

                // Copy Symbol properties
                if (contextAncestorBindingInfo in parentContext) {
                    self[contextAncestorBindingInfo] = parentContext[contextAncestorBindingInfo];
                }
            } else {
                self['$parents'] = [];
                self['$root'] = dataItem;

                // Export 'ko' in the binding context so it will be available in bindings and templates
                // even if 'ko' isn't exported as a global, such as when using an AMD loader.
                // See https://github.com/SteveSanderson/knockout/issues/490
                self['ko'] = ko;
            }

            self[contextSubscribable] = subscribable;

            if (shouldInheritData) {
                dataItem = self['$data'];
            } else {
                self['$rawData'] = dataItemOrObservable;
                self['$data'] = dataItem;
            }

            if (dataItemAlias)
                self[dataItemAlias] = dataItem;

            // The extendCallback function is provided when creating a child context or extending a context.
            // It handles the specific actions needed to finish setting up the binding context. Actions in this
            // function could also add dependencies to this binding context.
            if (extendCallback)
                extendCallback(self, parentContext, dataItem);

            // When a "parent" context is given and we don't already have a dependency on its context, register a dependency on it.
            // Thus whenever the parent context is updated, this context will also be updated.
            if (parentContext && parentContext[contextSubscribable] && !ko.computedContext.computed().hasAncestorDependency(parentContext[contextSubscribable])) {
                parentContext[contextSubscribable]();
            }

            if (dataDependency) {
                self[contextDataDependency] = dataDependency;
            }

            return self['$data'];
        }

        var self = this,
            shouldInheritData = dataItemOrAccessor === inheritParentVm,
            realDataItemOrAccessor = shouldInheritData ? undefined : dataItemOrAccessor,
            isFunc = typeof(realDataItemOrAccessor) == "function" && !ko.isObservable(realDataItemOrAccessor),
            nodes,
            subscribable,
            dataDependency = options && options['dataDependency'];

        if (options && options['exportDependencies']) {
            // The "exportDependencies" option means that the calling code will track any dependencies and re-create
            // the binding context when they change.
            updateContext();
        } else {
            subscribable = ko.pureComputed(updateContext);
            subscribable.peek();

            // At this point, the binding context has been initialized, and the "subscribable" computed observable is
            // subscribed to any observables that were accessed in the process. If there is nothing to track, the
            // computed will be inactive, and we can safely throw it away. If it's active, the computed is stored in
            // the context object.
            if (subscribable.isActive()) {
                // Always notify because even if the model ($data) hasn't changed, other context properties might have changed
                subscribable['equalityComparer'] = null;
            } else {
                self[contextSubscribable] = undefined;
            }
        }
    }

    // Extend the binding context hierarchy with a new view model object. If the parent context is watching
    // any observables, the new child context will automatically get a dependency on the parent context.
    // But this does not mean that the $data value of the child context will also get updated. If the child
    // view model also depends on the parent view model, you must provide a function that returns the correct
    // view model on each update.
    ko.bindingContext.prototype['createChildContext'] = function (dataItemOrAccessor, dataItemAlias, extendCallback, options) {
        if (!options && dataItemAlias && typeof dataItemAlias == "object") {
            options = dataItemAlias;
            dataItemAlias = options['as'];
            extendCallback = options['extend'];
        }

        if (dataItemAlias && options && options['noChildContext']) {
            var isFunc = typeof(dataItemOrAccessor) == "function" && !ko.isObservable(dataItemOrAccessor);
            return new ko.bindingContext(inheritParentVm, this, null, function (self) {
                if (extendCallback)
                    extendCallback(self);
                self[dataItemAlias] = isFunc ? dataItemOrAccessor() : dataItemOrAccessor;
            }, options);
        }

        return new ko.bindingContext(dataItemOrAccessor, this, dataItemAlias, function (self, parentContext) {
            // Extend the context hierarchy by setting the appropriate pointers
            self['$parentContext'] = parentContext;
            self['$parent'] = parentContext['$data'];
            self['$parents'] = (parentContext['$parents'] || []).slice(0);
            self['$parents'].unshift(self['$parent']);
            if (extendCallback)
                extendCallback(self);
        }, options);
    };

    // Extend the binding context with new custom properties. This doesn't change the context hierarchy.
    // Similarly to "child" contexts, provide a function here to make sure that the correct values are set
    // when an observable view model is updated.
    ko.bindingContext.prototype['extend'] = function(properties, options) {
        return new ko.bindingContext(inheritParentVm, this, null, function(self, parentContext) {
            ko.utils.extend(self, typeof(properties) == "function" ? properties(self) : properties);
        }, options);
    };

    var boundElementDomDataKey = ko.utils.domData.nextKey();

    function asyncContextDispose(node) {
        var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey),
            asyncContext = bindingInfo && bindingInfo.asyncContext;
        if (asyncContext) {
            bindingInfo.asyncContext = null;
            asyncContext.notifyAncestor();
        }
    }
    function AsyncCompleteContext(node, bindingInfo, ancestorBindingInfo) {
        this.node = node;
        this.bindingInfo = bindingInfo;
        this.asyncDescendants = [];
        this.childrenComplete = false;

        if (!bindingInfo.asyncContext) {
            ko.utils.domNodeDisposal.addDisposeCallback(node, asyncContextDispose);
        }

        if (ancestorBindingInfo && ancestorBindingInfo.asyncContext) {
            ancestorBindingInfo.asyncContext.asyncDescendants.push(node);
            this.ancestorBindingInfo = ancestorBindingInfo;
        }
    }
    AsyncCompleteContext.prototype.notifyAncestor = function () {
        if (this.ancestorBindingInfo && this.ancestorBindingInfo.asyncContext) {
            this.ancestorBindingInfo.asyncContext.descendantComplete(this.node);
        }
    };
    AsyncCompleteContext.prototype.descendantComplete = function (node) {
        ko.utils.arrayRemoveItem(this.asyncDescendants, node);
        if (!this.asyncDescendants.length && this.childrenComplete) {
            this.completeChildren();
        }
    };
    AsyncCompleteContext.prototype.completeChildren = function () {
        this.childrenComplete = true;
        if (this.bindingInfo.asyncContext && !this.asyncDescendants.length) {
            this.bindingInfo.asyncContext = null;
            ko.utils.domNodeDisposal.removeDisposeCallback(this.node, asyncContextDispose);
            ko.bindingEvent.notify(this.node, ko.bindingEvent.descendantsComplete);
            this.notifyAncestor();
        }
    };

    ko.bindingEvent = {
        childrenComplete: "childrenComplete",
        descendantsComplete : "descendantsComplete",

        subscribe: function (node, event, callback, context, options) {
            var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});
            if (!bindingInfo.eventSubscribable) {
                bindingInfo.eventSubscribable = new ko.subscribable;
            }
            if (options && options['notifyImmediately'] && bindingInfo.notifiedEvents[event]) {
                ko.dependencyDetection.ignore(callback, context, [node]);
            }
            return bindingInfo.eventSubscribable.subscribe(callback, context, event);
        },

        notify: function (node, event) {
            var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
            if (bindingInfo) {
                bindingInfo.notifiedEvents[event] = true;
                if (bindingInfo.eventSubscribable) {
                    bindingInfo.eventSubscribable['notifySubscribers'](node, event);
                }
                if (event == ko.bindingEvent.childrenComplete) {
                    if (bindingInfo.asyncContext) {
                        bindingInfo.asyncContext.completeChildren();
                    } else if (bindingInfo.asyncContext === undefined && bindingInfo.eventSubscribable && bindingInfo.eventSubscribable.hasSubscriptionsForEvent(ko.bindingEvent.descendantsComplete)) {
                        // It's currently an error to register a descendantsComplete handler for a node that was never registered as completing asynchronously.
                        // That's because without the asyncContext, we don't have a way to know that all descendants have completed.
                        throw new Error("descendantsComplete event not supported for bindings on this node");
                    }
                }
            }
        },

        startPossiblyAsyncContentBinding: function (node, bindingContext) {
            var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});

            if (!bindingInfo.asyncContext) {
                bindingInfo.asyncContext = new AsyncCompleteContext(node, bindingInfo, bindingContext[contextAncestorBindingInfo]);
            }

            // If the provided context was already extended with this node's binding info, just return the extended context
            if (bindingContext[contextAncestorBindingInfo] == bindingInfo) {
                return bindingContext;
            }

            return bindingContext['extend'](function (ctx) {
                ctx[contextAncestorBindingInfo] = bindingInfo;
            });
        }
    };

    // Returns the valueAccessor function for a binding value
    function makeValueAccessor(value) {
        return function() {
            return value;
        };
    }

    // Returns the value of a valueAccessor function
    function evaluateValueAccessor(valueAccessor) {
        return valueAccessor();
    }

    // Given a function that returns bindings, create and return a new object that contains
    // binding value-accessors functions. Each accessor function calls the original function
    // so that it always gets the latest value and all dependencies are captured. This is used
    // by ko.applyBindingsToNode and getBindingsAndMakeAccessors.
    function makeAccessorsFromFunction(callback) {
        return ko.utils.objectMap(ko.dependencyDetection.ignore(callback), function(value, key) {
            return function() {
                return callback()[key];
            };
        });
    }

    // Given a bindings function or object, create and return a new object that contains
    // binding value-accessors functions. This is used by ko.applyBindingsToNode.
    function makeBindingAccessors(bindings, context, node) {
        if (typeof bindings === 'function') {
            return makeAccessorsFromFunction(bindings.bind(null, context, node));
        } else {
            return ko.utils.objectMap(bindings, makeValueAccessor);
        }
    }

    // This function is used if the binding provider doesn't include a getBindingAccessors function.
    // It must be called with 'this' set to the provider instance.
    function getBindingsAndMakeAccessors(node, context) {
        return makeAccessorsFromFunction(this['getBindings'].bind(this, node, context));
    }

    function validateThatBindingIsAllowedForVirtualElements(bindingName) {
        var validator = ko.virtualElements.allowedBindings[bindingName];
        if (!validator)
            throw new Error("The binding '" + bindingName + "' cannot be used with virtual elements")
    }

    function applyBindingsToDescendantsInternal(bindingContext, elementOrVirtualElement) {
        var nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);

        if (nextInQueue) {
            var currentChild,
                provider = ko.bindingProvider['instance'],
                preprocessNode = provider['preprocessNode'];

            // Preprocessing allows a binding provider to mutate a node before bindings are applied to it. For example it's
            // possible to insert new siblings after it, and/or replace the node with a different one. This can be used to
            // implement custom binding syntaxes, such as {{ value }} for string interpolation, or custom element types that
            // trigger insertion of <template> contents at that point in the document.
            if (preprocessNode) {
                while (currentChild = nextInQueue) {
                    nextInQueue = ko.virtualElements.nextSibling(currentChild);
                    preprocessNode.call(provider, currentChild);
                }
                // Reset nextInQueue for the next loop
                nextInQueue = ko.virtualElements.firstChild(elementOrVirtualElement);
            }

            while (currentChild = nextInQueue) {
                // Keep a record of the next child *before* applying bindings, in case the binding removes the current child from its position
                nextInQueue = ko.virtualElements.nextSibling(currentChild);
                applyBindingsToNodeAndDescendantsInternal(bindingContext, currentChild);
            }
        }
        ko.bindingEvent.notify(elementOrVirtualElement, ko.bindingEvent.childrenComplete);
    }

    function applyBindingsToNodeAndDescendantsInternal(bindingContext, nodeVerified) {
        var bindingContextForDescendants = bindingContext;

        var isElement = (nodeVerified.nodeType === 1);
        if (isElement) // Workaround IE <= 8 HTML parsing weirdness
            ko.virtualElements.normaliseVirtualElementDomStructure(nodeVerified);

        // Perf optimisation: Apply bindings only if...
        // (1) We need to store the binding info for the node (all element nodes)
        // (2) It might have bindings (e.g., it has a data-bind attribute, or it's a marker for a containerless template)
        var shouldApplyBindings = isElement || ko.bindingProvider['instance']['nodeHasBindings'](nodeVerified);
        if (shouldApplyBindings)
            bindingContextForDescendants = applyBindingsToNodeInternal(nodeVerified, null, bindingContext)['bindingContextForDescendants'];

        if (bindingContextForDescendants && !bindingDoesNotRecurseIntoElementTypes[ko.utils.tagNameLower(nodeVerified)]) {
            applyBindingsToDescendantsInternal(bindingContextForDescendants, nodeVerified);
        }
    }

    function topologicalSortBindings(bindings) {
        // Depth-first sort
        var result = [],                // The list of key/handler pairs that we will return
            bindingsConsidered = {},    // A temporary record of which bindings are already in 'result'
            cyclicDependencyStack = []; // Keeps track of a depth-search so that, if there's a cycle, we know which bindings caused it
        ko.utils.objectForEach(bindings, function pushBinding(bindingKey) {
            if (!bindingsConsidered[bindingKey]) {
                var binding = ko['getBindingHandler'](bindingKey);
                if (binding) {
                    // First add dependencies (if any) of the current binding
                    if (binding['after']) {
                        cyclicDependencyStack.push(bindingKey);
                        ko.utils.arrayForEach(binding['after'], function(bindingDependencyKey) {
                            if (bindings[bindingDependencyKey]) {
                                if (ko.utils.arrayIndexOf(cyclicDependencyStack, bindingDependencyKey) !== -1) {
                                    throw Error("Cannot combine the following bindings, because they have a cyclic dependency: " + cyclicDependencyStack.join(", "));
                                } else {
                                    pushBinding(bindingDependencyKey);
                                }
                            }
                        });
                        cyclicDependencyStack.length--;
                    }
                    // Next add the current binding
                    result.push({ key: bindingKey, handler: binding });
                }
                bindingsConsidered[bindingKey] = true;
            }
        });

        return result;
    }

    function applyBindingsToNodeInternal(node, sourceBindings, bindingContext) {
        var bindingInfo = ko.utils.domData.getOrSet(node, boundElementDomDataKey, {});

        // Prevent multiple applyBindings calls for the same node, except when a binding value is specified
        var alreadyBound = bindingInfo.alreadyBound;
        if (!sourceBindings) {
            if (alreadyBound) {
                throw Error("You cannot apply bindings multiple times to the same element.");
            }
            bindingInfo.alreadyBound = true;
        }
        if (!alreadyBound) {
            bindingInfo.context = bindingContext;
        }
        if (!bindingInfo.notifiedEvents) {
            bindingInfo.notifiedEvents = {};
        }

        // Use bindings if given, otherwise fall back on asking the bindings provider to give us some bindings
        var bindings;
        if (sourceBindings && typeof sourceBindings !== 'function') {
            bindings = sourceBindings;
        } else {
            var provider = ko.bindingProvider['instance'],
                getBindings = provider['getBindingAccessors'] || getBindingsAndMakeAccessors;

            // Get the binding from the provider within a computed observable so that we can update the bindings whenever
            // the binding context is updated or if the binding provider accesses observables.
            var bindingsUpdater = ko.dependentObservable(
                function() {
                    bindings = sourceBindings ? sourceBindings(bindingContext, node) : getBindings.call(provider, node, bindingContext);
                    // Register a dependency on the binding context to support observable view models.
                    if (bindings) {
                        if (bindingContext[contextSubscribable]) {
                            bindingContext[contextSubscribable]();
                        }
                        if (bindingContext[contextDataDependency]) {
                            bindingContext[contextDataDependency]();
                        }
                    }
                    return bindings;
                },
                null, { disposeWhenNodeIsRemoved: node }
            );

            if (!bindings || !bindingsUpdater.isActive())
                bindingsUpdater = null;
        }

        var contextToExtend = bindingContext;
        var bindingHandlerThatControlsDescendantBindings;
        if (bindings) {
            // Return the value accessor for a given binding. When bindings are static (won't be updated because of a binding
            // context update), just return the value accessor from the binding. Otherwise, return a function that always gets
            // the latest binding value and registers a dependency on the binding updater.
            var getValueAccessor = bindingsUpdater
                ? function(bindingKey) {
                    return function() {
                        return evaluateValueAccessor(bindingsUpdater()[bindingKey]);
                    };
                } : function(bindingKey) {
                    return bindings[bindingKey];
                };

            // Use of allBindings as a function is maintained for backwards compatibility, but its use is deprecated
            function allBindings() {
                return ko.utils.objectMap(bindingsUpdater ? bindingsUpdater() : bindings, evaluateValueAccessor);
            }
            // The following is the 3.x allBindings API
            allBindings['get'] = function(key) {
                return bindings[key] && evaluateValueAccessor(getValueAccessor(key));
            };
            allBindings['has'] = function(key) {
                return key in bindings;
            };

            if (ko.bindingEvent.childrenComplete in bindings) {
                ko.bindingEvent.subscribe(node, ko.bindingEvent.childrenComplete, function () {
                    var callback = evaluateValueAccessor(bindings[ko.bindingEvent.childrenComplete]);
                    if (callback) {
                        var nodes = ko.virtualElements.childNodes(node);
                        if (nodes.length) {
                            callback(nodes, ko.dataFor(nodes[0]));
                        }
                    }
                });
            }

            if (ko.bindingEvent.descendantsComplete in bindings) {
                contextToExtend = ko.bindingEvent.startPossiblyAsyncContentBinding(node, bindingContext);
                ko.bindingEvent.subscribe(node, ko.bindingEvent.descendantsComplete, function () {
                    var callback = evaluateValueAccessor(bindings[ko.bindingEvent.descendantsComplete]);
                    if (callback && ko.virtualElements.firstChild(node)) {
                        callback(node);
                    }
                });
            }

            // First put the bindings into the right order
            var orderedBindings = topologicalSortBindings(bindings);

            // Go through the sorted bindings, calling init and update for each
            ko.utils.arrayForEach(orderedBindings, function(bindingKeyAndHandler) {
                // Note that topologicalSortBindings has already filtered out any nonexistent binding handlers,
                // so bindingKeyAndHandler.handler will always be nonnull.
                var handlerInitFn = bindingKeyAndHandler.handler["init"],
                    handlerUpdateFn = bindingKeyAndHandler.handler["update"],
                    bindingKey = bindingKeyAndHandler.key;

                if (node.nodeType === 8) {
                    validateThatBindingIsAllowedForVirtualElements(bindingKey);
                }

                try {
                    // Run init, ignoring any dependencies
                    if (typeof handlerInitFn == "function") {
                        ko.dependencyDetection.ignore(function() {
                            var initResult = handlerInitFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);

                            // If this binding handler claims to control descendant bindings, make a note of this
                            if (initResult && initResult['controlsDescendantBindings']) {
                                if (bindingHandlerThatControlsDescendantBindings !== undefined)
                                    throw new Error("Multiple bindings (" + bindingHandlerThatControlsDescendantBindings + " and " + bindingKey + ") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
                                bindingHandlerThatControlsDescendantBindings = bindingKey;
                            }
                        });
                    }

                    // Run update in its own computed wrapper
                    if (typeof handlerUpdateFn == "function") {
                        ko.dependentObservable(
                            function() {
                                handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend);
                            },
                            null,
                            { disposeWhenNodeIsRemoved: node }
                        );
                    }
                } catch (ex) {
                    ex.message = "Unable to process binding \"" + bindingKey + ": " + bindings[bindingKey] + "\"\nMessage: " + ex.message;
                    throw ex;
                }
            });
        }

        var shouldBindDescendants = bindingHandlerThatControlsDescendantBindings === undefined;
        return {
            'shouldBindDescendants': shouldBindDescendants,
            'bindingContextForDescendants': shouldBindDescendants && contextToExtend
        };
    };

    ko.storedBindingContextForNode = function (node) {
        var bindingInfo = ko.utils.domData.get(node, boundElementDomDataKey);
        return bindingInfo && bindingInfo.context;
    }

    function getBindingContext(viewModelOrBindingContext, extendContextCallback) {
        return viewModelOrBindingContext && (viewModelOrBindingContext instanceof ko.bindingContext)
            ? viewModelOrBindingContext
            : new ko.bindingContext(viewModelOrBindingContext, undefined, undefined, extendContextCallback);
    }

    ko.applyBindingAccessorsToNode = function (node, bindings, viewModelOrBindingContext) {
        if (node.nodeType === 1) // If it's an element, workaround IE <= 8 HTML parsing weirdness
            ko.virtualElements.normaliseVirtualElementDomStructure(node);
        return applyBindingsToNodeInternal(node, bindings, getBindingContext(viewModelOrBindingContext));
    };

    ko.applyBindingsToNode = function (node, bindings, viewModelOrBindingContext) {
        var context = getBindingContext(viewModelOrBindingContext);
        return ko.applyBindingAccessorsToNode(node, makeBindingAccessors(bindings, context, node), context);
    };

    ko.applyBindingsToDescendants = function(viewModelOrBindingContext, rootNode) {
        if (rootNode.nodeType === 1 || rootNode.nodeType === 8)
            applyBindingsToDescendantsInternal(getBindingContext(viewModelOrBindingContext), rootNode);
    };

    ko.applyBindings = function (viewModelOrBindingContext, rootNode, extendContextCallback) {
        // If jQuery is loaded after Knockout, we won't initially have access to it. So save it here.
        if (!jQueryInstance && window['jQuery']) {
            jQueryInstance = window['jQuery'];
        }

        if (arguments.length < 2) {
            rootNode = document.body;
            if (!rootNode) {
                throw Error("ko.applyBindings: could not find document.body; has the document been loaded?");
            }
        } else if (!rootNode || (rootNode.nodeType !== 1 && rootNode.nodeType !== 8)) {
            throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");
        }

        applyBindingsToNodeAndDescendantsInternal(getBindingContext(viewModelOrBindingContext, extendContextCallback), rootNode);
    };

    // Retrieving binding context from arbitrary nodes
    ko.contextFor = function(node) {
        // We can only do something meaningful for elements and comment nodes (in particular, not text nodes, as IE can't store domdata for them)
        if (node && (node.nodeType === 1 || node.nodeType === 8)) {
            return ko.storedBindingContextForNode(node);
        }
        return undefined;
    };
    ko.dataFor = function(node) {
        var context = ko.contextFor(node);
        return context ? context['$data'] : undefined;
    };

    ko.exportSymbol('bindingHandlers', ko.bindingHandlers);
    ko.exportSymbol('bindingEvent', ko.bindingEvent);
    ko.exportSymbol('bindingEvent.subscribe', ko.bindingEvent.subscribe);
    ko.exportSymbol('bindingEvent.startPossiblyAsyncContentBinding', ko.bindingEvent.startPossiblyAsyncContentBinding);
    ko.exportSymbol('applyBindings', ko.applyBindings);
    ko.exportSymbol('applyBindingsToDescendants', ko.applyBindingsToDescendants);
    ko.exportSymbol('applyBindingAccessorsToNode', ko.applyBindingAccessorsToNode);
    ko.exportSymbol('applyBindingsToNode', ko.applyBindingsToNode);
    ko.exportSymbol('contextFor', ko.contextFor);
    ko.exportSymbol('dataFor', ko.dataFor);
})();
(function(undefined) {
    var loadingSubscribablesCache = {}, // Tracks component loads that are currently in flight
        loadedDefinitionsCache = {};    // Tracks component loads that have already completed

    ko.components = {
        get: function(componentName, callback) {
            var cachedDefinition = getObjectOwnProperty(loadedDefinitionsCache, componentName);
            if (cachedDefinition) {
                // It's already loaded and cached. Reuse the same definition object.
                // Note that for API consistency, even cache hits complete asynchronously by default.
                // You can bypass this by putting synchronous:true on your component config.
                if (cachedDefinition.isSynchronousComponent) {
                    ko.dependencyDetection.ignore(function() { // See comment in loaderRegistryBehaviors.js for reasoning
                        callback(cachedDefinition.definition);
                    });
                } else {
                    ko.tasks.schedule(function() { callback(cachedDefinition.definition); });
                }
            } else {
                // Join the loading process that is already underway, or start a new one.
                loadComponentAndNotify(componentName, callback);
            }
        },

        clearCachedDefinition: function(componentName) {
            delete loadedDefinitionsCache[componentName];
        },

        _getFirstResultFromLoaders: getFirstResultFromLoaders
    };

    function getObjectOwnProperty(obj, propName) {
        return Object.prototype.hasOwnProperty.call(obj, propName) ? obj[propName] : undefined;
    }

    function loadComponentAndNotify(componentName, callback) {
        var subscribable = getObjectOwnProperty(loadingSubscribablesCache, componentName),
            completedAsync;
        if (!subscribable) {
            // It's not started loading yet. Start loading, and when it's done, move it to loadedDefinitionsCache.
            subscribable = loadingSubscribablesCache[componentName] = new ko.subscribable();
            subscribable.subscribe(callback);

            beginLoadingComponent(componentName, function(definition, config) {
                var isSynchronousComponent = !!(config && config['synchronous']);
                loadedDefinitionsCache[componentName] = { definition: definition, isSynchronousComponent: isSynchronousComponent };
                delete loadingSubscribablesCache[componentName];

                // For API consistency, all loads complete asynchronously. However we want to avoid
                // adding an extra task schedule if it's unnecessary (i.e., the completion is already
                // async).
                //
                // You can bypass the 'always asynchronous' feature by putting the synchronous:true
                // flag on your component configuration when you register it.
                if (completedAsync || isSynchronousComponent) {
                    // Note that notifySubscribers ignores any dependencies read within the callback.
                    // See comment in loaderRegistryBehaviors.js for reasoning
                    subscribable['notifySubscribers'](definition);
                } else {
                    ko.tasks.schedule(function() {
                        subscribable['notifySubscribers'](definition);
                    });
                }
            });
            completedAsync = true;
        } else {
            subscribable.subscribe(callback);
        }
    }

    function beginLoadingComponent(componentName, callback) {
        getFirstResultFromLoaders('getConfig', [componentName], function(config) {
            if (config) {
                // We have a config, so now load its definition
                getFirstResultFromLoaders('loadComponent', [componentName, config], function(definition) {
                    callback(definition, config);
                });
            } else {
                // The component has no config - it's unknown to all the loaders.
                // Note that this is not an error (e.g., a module loading error) - that would abort the
                // process and this callback would not run. For this callback to run, all loaders must
                // have confirmed they don't know about this component.
                callback(null, null);
            }
        });
    }

    function getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders) {
        // On the first call in the stack, start with the full set of loaders
        if (!candidateLoaders) {
            candidateLoaders = ko.components['loaders'].slice(0); // Use a copy, because we'll be mutating this array
        }

        // Try the next candidate
        var currentCandidateLoader = candidateLoaders.shift();
        if (currentCandidateLoader) {
            var methodInstance = currentCandidateLoader[methodName];
            if (methodInstance) {
                var wasAborted = false,
                    synchronousReturnValue = methodInstance.apply(currentCandidateLoader, argsExceptCallback.concat(function(result) {
                        if (wasAborted) {
                            callback(null);
                        } else if (result !== null) {
                            // This candidate returned a value. Use it.
                            callback(result);
                        } else {
                            // Try the next candidate
                            getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);
                        }
                    }));

                // Currently, loaders may not return anything synchronously. This leaves open the possibility
                // that we'll extend the API to support synchronous return values in the future. It won't be
                // a breaking change, because currently no loader is allowed to return anything except undefined.
                if (synchronousReturnValue !== undefined) {
                    wasAborted = true;

                    // Method to suppress exceptions will remain undocumented. This is only to keep
                    // KO's specs running tidily, since we can observe the loading got aborted without
                    // having exceptions cluttering up the console too.
                    if (!currentCandidateLoader['suppressLoaderExceptions']) {
                        throw new Error('Component loaders must supply values by invoking the callback, not by returning values synchronously.');
                    }
                }
            } else {
                // This candidate doesn't have the relevant handler. Synchronously move on to the next one.
                getFirstResultFromLoaders(methodName, argsExceptCallback, callback, candidateLoaders);
            }
        } else {
            // No candidates returned a value
            callback(null);
        }
    }

    // Reference the loaders via string name so it's possible for developers
    // to replace the whole array by assigning to ko.components.loaders
    ko.components['loaders'] = [];

    ko.exportSymbol('components', ko.components);
    ko.exportSymbol('components.get', ko.components.get);
    ko.exportSymbol('components.clearCachedDefinition', ko.components.clearCachedDefinition);
})();
(function(undefined) {

    // The default loader is responsible for two things:
    // 1. Maintaining the default in-memory registry of component configuration objects
    //    (i.e., the thing you're writing to when you call ko.components.register(someName, ...))
    // 2. Answering requests for components by fetching configuration objects
    //    from that default in-memory registry and resolving them into standard
    //    component definition objects (of the form { createViewModel: ..., template: ... })
    // Custom loaders may override either of these facilities, i.e.,
    // 1. To supply configuration objects from some other source (e.g., conventions)
    // 2. Or, to resolve configuration objects by loading viewmodels/templates via arbitrary logic.

    var defaultConfigRegistry = {};

    ko.components.register = function(componentName, config) {
        if (!config) {
            throw new Error('Invalid configuration for ' + componentName);
        }

        if (ko.components.isRegistered(componentName)) {
            throw new Error('Component ' + componentName + ' is already registered');
        }

        defaultConfigRegistry[componentName] = config;
    };

    ko.components.isRegistered = function(componentName) {
        return Object.prototype.hasOwnProperty.call(defaultConfigRegistry, componentName);
    };

    ko.components.unregister = function(componentName) {
        delete defaultConfigRegistry[componentName];
        ko.components.clearCachedDefinition(componentName);
    };

    ko.components.defaultLoader = {
        'getConfig': function(componentName, callback) {
            var result = ko.components.isRegistered(componentName)
                ? defaultConfigRegistry[componentName]
                : null;
            callback(result);
        },

        'loadComponent': function(componentName, config, callback) {
            var errorCallback = makeErrorCallback(componentName);
            possiblyGetConfigFromAmd(errorCallback, config, function(loadedConfig) {
                resolveConfig(componentName, errorCallback, loadedConfig, callback);
            });
        },

        'loadTemplate': function(componentName, templateConfig, callback) {
            resolveTemplate(makeErrorCallback(componentName), templateConfig, callback);
        },

        'loadViewModel': function(componentName, viewModelConfig, callback) {
            resolveViewModel(makeErrorCallback(componentName), viewModelConfig, callback);
        }
    };

    var createViewModelKey = 'createViewModel';

    // Takes a config object of the form { template: ..., viewModel: ... }, and asynchronously convert it
    // into the standard component definition format:
    //    { template: <ArrayOfDomNodes>, createViewModel: function(params, componentInfo) { ... } }.
    // Since both template and viewModel may need to be resolved asynchronously, both tasks are performed
    // in parallel, and the results joined when both are ready. We don't depend on any promises infrastructure,
    // so this is implemented manually below.
    function resolveConfig(componentName, errorCallback, config, callback) {
        var result = {},
            makeCallBackWhenZero = 2,
            tryIssueCallback = function() {
                if (--makeCallBackWhenZero === 0) {
                    callback(result);
                }
            },
            templateConfig = config['template'],
            viewModelConfig = config['viewModel'];

        if (templateConfig) {
            possiblyGetConfigFromAmd(errorCallback, templateConfig, function(loadedConfig) {
                ko.components._getFirstResultFromLoaders('loadTemplate', [componentName, loadedConfig], function(resolvedTemplate) {
                    result['template'] = resolvedTemplate;
                    tryIssueCallback();
                });
            });
        } else {
            tryIssueCallback();
        }

        if (viewModelConfig) {
            possiblyGetConfigFromAmd(errorCallback, viewModelConfig, function(loadedConfig) {
                ko.components._getFirstResultFromLoaders('loadViewModel', [componentName, loadedConfig], function(resolvedViewModel) {
                    result[createViewModelKey] = resolvedViewModel;
                    tryIssueCallback();
                });
            });
        } else {
            tryIssueCallback();
        }
    }

    function resolveTemplate(errorCallback, templateConfig, callback) {
        if (typeof templateConfig === 'string') {
            // Markup - parse it
            callback(ko.utils.parseHtmlFragment(templateConfig));
        } else if (templateConfig instanceof Array) {
            // Assume already an array of DOM nodes - pass through unchanged
            callback(templateConfig);
        } else if (isDocumentFragment(templateConfig)) {
            // Document fragment - use its child nodes
            callback(ko.utils.makeArray(templateConfig.childNodes));
        } else if (templateConfig['element']) {
            var element = templateConfig['element'];
            if (isDomElement(element)) {
                // Element instance - copy its child nodes
                callback(cloneNodesFromTemplateSourceElement(element));
            } else if (typeof element === 'string') {
                // Element ID - find it, then copy its child nodes
                var elemInstance = document.getElementById(element);
                if (elemInstance) {
                    callback(cloneNodesFromTemplateSourceElement(elemInstance));
                } else {
                    errorCallback('Cannot find element with ID ' + element);
                }
            } else {
                errorCallback('Unknown element type: ' + element);
            }
        } else {
            errorCallback('Unknown template value: ' + templateConfig);
        }
    }

    function resolveViewModel(errorCallback, viewModelConfig, callback) {
        if (typeof viewModelConfig === 'function') {
            // Constructor - convert to standard factory function format
            // By design, this does *not* supply componentInfo to the constructor, as the intent is that
            // componentInfo contains non-viewmodel data (e.g., the component's element) that should only
            // be used in factory functions, not viewmodel constructors.
            callback(function (params /*, componentInfo */) {
                return new viewModelConfig(params);
            });
        } else if (typeof viewModelConfig[createViewModelKey] === 'function') {
            // Already a factory function - use it as-is
            callback(viewModelConfig[createViewModelKey]);
        } else if ('instance' in viewModelConfig) {
            // Fixed object instance - promote to createViewModel format for API consistency
            var fixedInstance = viewModelConfig['instance'];
            callback(function (params, componentInfo) {
                return fixedInstance;
            });
        } else if ('viewModel' in viewModelConfig) {
            // Resolved AMD module whose value is of the form { viewModel: ... }
            resolveViewModel(errorCallback, viewModelConfig['viewModel'], callback);
        } else {
            errorCallback('Unknown viewModel value: ' + viewModelConfig);
        }
    }

    function cloneNodesFromTemplateSourceElement(elemInstance) {
        switch (ko.utils.tagNameLower(elemInstance)) {
            case 'script':
                return ko.utils.parseHtmlFragment(elemInstance.text);
            case 'textarea':
                return ko.utils.parseHtmlFragment(elemInstance.value);
            case 'template':
                // For browsers with proper <template> element support (i.e., where the .content property
                // gives a document fragment), use that document fragment.
                if (isDocumentFragment(elemInstance.content)) {
                    return ko.utils.cloneNodes(elemInstance.content.childNodes);
                }
        }

        // Regular elements such as <div>, and <template> elements on old browsers that don't really
        // understand <template> and just treat it as a regular container
        return ko.utils.cloneNodes(elemInstance.childNodes);
    }

    function isDomElement(obj) {
        if (window['HTMLElement']) {
            return obj instanceof HTMLElement;
        } else {
            return obj && obj.tagName && obj.nodeType === 1;
        }
    }

    function isDocumentFragment(obj) {
        if (window['DocumentFragment']) {
            return obj instanceof DocumentFragment;
        } else {
            return obj && obj.nodeType === 11;
        }
    }

    function possiblyGetConfigFromAmd(errorCallback, config, callback) {
        if (typeof config['require'] === 'string') {
            // The config is the value of an AMD module
            if (amdRequire || window['require']) {
                (amdRequire || window['require'])([config['require']], function (module) {
                    if (module && typeof module === 'object' && module.__esModule && module.default) {
                        module = module.default;
                    }
                    callback(module);
                });
            } else {
                errorCallback('Uses require, but no AMD loader is present');
            }
        } else {
            callback(config);
        }
    }

    function makeErrorCallback(componentName) {
        return function (message) {
            throw new Error('Component \'' + componentName + '\': ' + message);
        };
    }

    ko.exportSymbol('components.register', ko.components.register);
    ko.exportSymbol('components.isRegistered', ko.components.isRegistered);
    ko.exportSymbol('components.unregister', ko.components.unregister);

    // Expose the default loader so that developers can directly ask it for configuration
    // or to resolve configuration
    ko.exportSymbol('components.defaultLoader', ko.components.defaultLoader);

    // By default, the default loader is the only registered component loader
    ko.components['loaders'].push(ko.components.defaultLoader);

    // Privately expose the underlying config registry for use in old-IE shim
    ko.components._allRegisteredComponents = defaultConfigRegistry;
})();
(function (undefined) {
    // Overridable API for determining which component name applies to a given node. By overriding this,
    // you can for example map specific tagNames to components that are not preregistered.
    ko.components['getComponentNameForNode'] = function(node) {
        var tagNameLower = ko.utils.tagNameLower(node);
        if (ko.components.isRegistered(tagNameLower)) {
            // Try to determine that this node can be considered a *custom* element; see https://github.com/knockout/knockout/issues/1603
            if (tagNameLower.indexOf('-') != -1 || ('' + node) == "[object HTMLUnknownElement]" || (ko.utils.ieVersion <= 8 && node.tagName === tagNameLower)) {
                return tagNameLower;
            }
        }
    };

    ko.components.addBindingsForCustomElement = function(allBindings, node, bindingContext, valueAccessors) {
        // Determine if it's really a custom element matching a component
        if (node.nodeType === 1) {
            var componentName = ko.components['getComponentNameForNode'](node);
            if (componentName) {
                // It does represent a component, so add a component binding for it
                allBindings = allBindings || {};

                if (allBindings['component']) {
                    // Avoid silently overwriting some other 'component' binding that may already be on the element
                    throw new Error('Cannot use the "component" binding on a custom element matching a component');
                }

                var componentBindingValue = { 'name': componentName, 'params': getComponentParamsFromCustomElement(node, bindingContext) };

                allBindings['component'] = valueAccessors
                    ? function() { return componentBindingValue; }
                    : componentBindingValue;
            }
        }

        return allBindings;
    }

    var nativeBindingProviderInstance = new ko.bindingProvider();

    function getComponentParamsFromCustomElement(elem, bindingContext) {
        var paramsAttribute = elem.getAttribute('params');

        if (paramsAttribute) {
            var params = nativeBindingProviderInstance['parseBindingsString'](paramsAttribute, bindingContext, elem, { 'valueAccessors': true, 'bindingParams': true }),
                rawParamComputedValues = ko.utils.objectMap(params, function(paramValue, paramName) {
                    return ko.computed(paramValue, null, { disposeWhenNodeIsRemoved: elem });
                }),
                result = ko.utils.objectMap(rawParamComputedValues, function(paramValueComputed, paramName) {
                    var paramValue = paramValueComputed.peek();
                    // Does the evaluation of the parameter value unwrap any observables?
                    if (!paramValueComputed.isActive()) {
                        // No it doesn't, so there's no need for any computed wrapper. Just pass through the supplied value directly.
                        // Example: "someVal: firstName, age: 123" (whether or not firstName is an observable/computed)
                        return paramValue;
                    } else {
                        // Yes it does. Supply a computed property that unwraps both the outer (binding expression)
                        // level of observability, and any inner (resulting model value) level of observability.
                        // This means the component doesn't have to worry about multiple unwrapping. If the value is a
                        // writable observable, the computed will also be writable and pass the value on to the observable.
                        return ko.computed({
                            'read': function() {
                                return ko.utils.unwrapObservable(paramValueComputed());
                            },
                            'write': ko.isWriteableObservable(paramValue) && function(value) {
                                paramValueComputed()(value);
                            },
                            disposeWhenNodeIsRemoved: elem
                        });
                    }
                });

            // Give access to the raw computeds, as long as that wouldn't overwrite any custom param also called '$raw'
            // This is in case the developer wants to react to outer (binding) observability separately from inner
            // (model value) observability, or in case the model value observable has subobservables.
            if (!Object.prototype.hasOwnProperty.call(result, '$raw')) {
                result['$raw'] = rawParamComputedValues;
            }

            return result;
        } else {
            // For consistency, absence of a "params" attribute is treated the same as the presence of
            // any empty one. Otherwise component viewmodels need special code to check whether or not
            // 'params' or 'params.$raw' is null/undefined before reading subproperties, which is annoying.
            return { '$raw': {} };
        }
    }

    // --------------------------------------------------------------------------------
    // Compatibility code for older (pre-HTML5) IE browsers

    if (ko.utils.ieVersion < 9) {
        // Whenever you preregister a component, enable it as a custom element in the current document
        ko.components['register'] = (function(originalFunction) {
            return function(componentName) {
                document.createElement(componentName); // Allows IE<9 to parse markup containing the custom element
                return originalFunction.apply(this, arguments);
            }
        })(ko.components['register']);

        // Whenever you create a document fragment, enable all preregistered component names as custom elements
        // This is needed to make innerShiv/jQuery HTML parsing correctly handle the custom elements
        document.createDocumentFragment = (function(originalFunction) {
            return function() {
                var newDocFrag = originalFunction(),
                    allComponents = ko.components._allRegisteredComponents;
                for (var componentName in allComponents) {
                    if (Object.prototype.hasOwnProperty.call(allComponents, componentName)) {
                        newDocFrag.createElement(componentName);
                    }
                }
                return newDocFrag;
            };
        })(document.createDocumentFragment);
    }
})();(function(undefined) {
    var componentLoadingOperationUniqueId = 0;

    ko.bindingHandlers['component'] = {
        'init': function(element, valueAccessor, ignored1, ignored2, bindingContext) {
            var currentViewModel,
                currentLoadingOperationId,
                afterRenderSub,
                disposeAssociatedComponentViewModel = function () {
                    var currentViewModelDispose = currentViewModel && currentViewModel['dispose'];
                    if (typeof currentViewModelDispose === 'function') {
                        currentViewModelDispose.call(currentViewModel);
                    }
                    if (afterRenderSub) {
                        afterRenderSub.dispose();
                    }
                    afterRenderSub = null;
                    currentViewModel = null;
                    // Any in-flight loading operation is no longer relevant, so make sure we ignore its completion
                    currentLoadingOperationId = null;
                },
                originalChildNodes = ko.utils.makeArray(ko.virtualElements.childNodes(element));

            ko.virtualElements.emptyNode(element);
            ko.utils.domNodeDisposal.addDisposeCallback(element, disposeAssociatedComponentViewModel);

            ko.computed(function () {
                var value = ko.utils.unwrapObservable(valueAccessor()),
                    componentName, componentParams;

                if (typeof value === 'string') {
                    componentName = value;
                } else {
                    componentName = ko.utils.unwrapObservable(value['name']);
                    componentParams = ko.utils.unwrapObservable(value['params']);
                }

                if (!componentName) {
                    throw new Error('No component name specified');
                }

                var asyncContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);

                var loadingOperationId = currentLoadingOperationId = ++componentLoadingOperationUniqueId;
                ko.components.get(componentName, function(componentDefinition) {
                    // If this is not the current load operation for this element, ignore it.
                    if (currentLoadingOperationId !== loadingOperationId) {
                        return;
                    }

                    // Clean up previous state
                    disposeAssociatedComponentViewModel();

                    // Instantiate and bind new component. Implicitly this cleans any old DOM nodes.
                    if (!componentDefinition) {
                        throw new Error('Unknown component \'' + componentName + '\'');
                    }
                    cloneTemplateIntoElement(componentName, componentDefinition, element);

                    var componentInfo = {
                        'element': element,
                        'templateNodes': originalChildNodes
                    };

                    var componentViewModel = createViewModel(componentDefinition, componentParams, componentInfo),
                        childBindingContext = asyncContext['createChildContext'](componentViewModel, {
                            'extend': function(ctx) {
                                ctx['$component'] = componentViewModel;
                                ctx['$componentTemplateNodes'] = originalChildNodes;
                            }
                        });

                    if (componentViewModel && componentViewModel['koDescendantsComplete']) {
                        afterRenderSub = ko.bindingEvent.subscribe(element, ko.bindingEvent.descendantsComplete, componentViewModel['koDescendantsComplete'], componentViewModel);
                    }

                    currentViewModel = componentViewModel;
                    ko.applyBindingsToDescendants(childBindingContext, element);
                });
            }, null, { disposeWhenNodeIsRemoved: element });

            return { 'controlsDescendantBindings': true };
        }
    };

    ko.virtualElements.allowedBindings['component'] = true;

    function cloneTemplateIntoElement(componentName, componentDefinition, element) {
        var template = componentDefinition['template'];
        if (!template) {
            throw new Error('Component \'' + componentName + '\' has no template');
        }

        var clonedNodesArray = ko.utils.cloneNodes(template);
        ko.virtualElements.setDomNodeChildren(element, clonedNodesArray);
    }

    function createViewModel(componentDefinition, componentParams, componentInfo) {
        var componentViewModelFactory = componentDefinition['createViewModel'];
        return componentViewModelFactory
            ? componentViewModelFactory.call(componentDefinition, componentParams, componentInfo)
            : componentParams; // Template-only component
    }

})();
var attrHtmlToJavaScriptMap = { 'class': 'className', 'for': 'htmlFor' };
ko.bindingHandlers['attr'] = {
    'update': function(element, valueAccessor, allBindings) {
        var value = ko.utils.unwrapObservable(valueAccessor()) || {};
        ko.utils.objectForEach(value, function(attrName, attrValue) {
            attrValue = ko.utils.unwrapObservable(attrValue);

            // Find the namespace of this attribute, if any.
            var prefixLen = attrName.indexOf(':');
            var namespace = "lookupNamespaceURI" in element && prefixLen > 0 && element.lookupNamespaceURI(attrName.substr(0, prefixLen));

            // To cover cases like "attr: { checked:someProp }", we want to remove the attribute entirely
            // when someProp is a "no value"-like value (strictly null, false, or undefined)
            // (because the absence of the "checked" attr is how to mark an element as not checked, etc.)
            var toRemove = (attrValue === false) || (attrValue === null) || (attrValue === undefined);
            if (toRemove) {
                namespace ? element.removeAttributeNS(namespace, attrName) : element.removeAttribute(attrName);
            } else {
                attrValue = attrValue.toString();
            }

            // In IE <= 7 and IE8 Quirks Mode, you have to use the JavaScript property name instead of the
            // HTML attribute name for certain attributes. IE8 Standards Mode supports the correct behavior,
            // but instead of figuring out the mode, we'll just set the attribute through the JavaScript
            // property for IE <= 8.
            if (ko.utils.ieVersion <= 8 && attrName in attrHtmlToJavaScriptMap) {
                attrName = attrHtmlToJavaScriptMap[attrName];
                if (toRemove)
                    element.removeAttribute(attrName);
                else
                    element[attrName] = attrValue;
            } else if (!toRemove) {
                namespace ? element.setAttributeNS(namespace, attrName, attrValue) : element.setAttribute(attrName, attrValue);
            }

            // Treat "name" specially - although you can think of it as an attribute, it also needs
            // special handling on older versions of IE (https://github.com/SteveSanderson/knockout/pull/333)
            // Deliberately being case-sensitive here because XHTML would regard "Name" as a different thing
            // entirely, and there's no strong reason to allow for such casing in HTML.
            if (attrName === "name") {
                ko.utils.setElementName(element, toRemove ? "" : attrValue);
            }
        });
    }
};
(function() {

ko.bindingHandlers['checked'] = {
    'after': ['value', 'attr'],
    'init': function (element, valueAccessor, allBindings) {
        var checkedValue = ko.pureComputed(function() {
            // Treat "value" like "checkedValue" when it is included with "checked" binding
            if (allBindings['has']('checkedValue')) {
                return ko.utils.unwrapObservable(allBindings.get('checkedValue'));
            } else if (useElementValue) {
                if (allBindings['has']('value')) {
                    return ko.utils.unwrapObservable(allBindings.get('value'));
                } else {
                    return element.value;
                }
            }
        });

        function updateModel() {
            // This updates the model value from the view value.
            // It runs in response to DOM events (click) and changes in checkedValue.
            var isChecked = element.checked,
                elemValue = checkedValue();

            // When we're first setting up this computed, don't change any model state.
            if (ko.computedContext.isInitial()) {
                return;
            }

            // We can ignore unchecked radio buttons, because some other radio
            // button will be checked, and that one can take care of updating state.
            // Also ignore value changes to an already unchecked checkbox.
            if (!isChecked && (isRadio || ko.computedContext.getDependenciesCount())) {
                return;
            }

            var modelValue = ko.dependencyDetection.ignore(valueAccessor);
            if (valueIsArray) {
                var writableValue = rawValueIsNonArrayObservable ? modelValue.peek() : modelValue,
                    saveOldValue = oldElemValue;
                oldElemValue = elemValue;

                if (saveOldValue !== elemValue) {
                    // When we're responding to the checkedValue changing, and the element is
                    // currently checked, replace the old elem value with the new elem value
                    // in the model array.
                    if (isChecked) {
                        ko.utils.addOrRemoveItem(writableValue, elemValue, true);
                        ko.utils.addOrRemoveItem(writableValue, saveOldValue, false);
                    }
                } else {
                    // When we're responding to the user having checked/unchecked a checkbox,
                    // add/remove the element value to the model array.
                    ko.utils.addOrRemoveItem(writableValue, elemValue, isChecked);
                }

                if (rawValueIsNonArrayObservable && ko.isWriteableObservable(modelValue)) {
                    modelValue(writableValue);
                }
            } else {
                if (isCheckbox) {
                    if (elemValue === undefined) {
                        elemValue = isChecked;
                    } else if (!isChecked) {
                        elemValue = undefined;
                    }
                }
                ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'checked', elemValue, true);
            }
        };

        function updateView() {
            // This updates the view value from the model value.
            // It runs in response to changes in the bound (checked) value.
            var modelValue = ko.utils.unwrapObservable(valueAccessor()),
                elemValue = checkedValue();

            if (valueIsArray) {
                // When a checkbox is bound to an array, being checked represents its value being present in that array
                element.checked = ko.utils.arrayIndexOf(modelValue, elemValue) >= 0;
                oldElemValue = elemValue;
            } else if (isCheckbox && elemValue === undefined) {
                // When a checkbox is bound to any other value (not an array) and "checkedValue" is not defined,
                // being checked represents the value being trueish
                element.checked = !!modelValue;
            } else {
                // Otherwise, being checked means that the checkbox or radio button's value corresponds to the model value
                element.checked = (checkedValue() === modelValue);
            }
        };

        var isCheckbox = element.type == "checkbox",
            isRadio = element.type == "radio";

        // Only bind to check boxes and radio buttons
        if (!isCheckbox && !isRadio) {
            return;
        }

        var rawValue = valueAccessor(),
            valueIsArray = isCheckbox && (ko.utils.unwrapObservable(rawValue) instanceof Array),
            rawValueIsNonArrayObservable = !(valueIsArray && rawValue.push && rawValue.splice),
            useElementValue = isRadio || valueIsArray,
            oldElemValue = valueIsArray ? checkedValue() : undefined;

        // IE 6 won't allow radio buttons to be selected unless they have a name
        if (isRadio && !element.name)
            ko.bindingHandlers['uniqueName']['init'](element, function() { return true });

        // Set up two computeds to update the binding:

        // The first responds to changes in the checkedValue value and to element clicks
        ko.computed(updateModel, null, { disposeWhenNodeIsRemoved: element });
        ko.utils.registerEventHandler(element, "click", updateModel);

        // The second responds to changes in the model value (the one associated with the checked binding)
        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });

        rawValue = undefined;
    }
};
ko.expressionRewriting.twoWayBindings['checked'] = true;

ko.bindingHandlers['checkedValue'] = {
    'update': function (element, valueAccessor) {
        element.value = ko.utils.unwrapObservable(valueAccessor());
    }
};

})();var classesWrittenByBindingKey = '__ko__cssValue';
ko.bindingHandlers['class'] = {
    'update': function (element, valueAccessor) {
        var value = ko.utils.stringTrim(ko.utils.unwrapObservable(valueAccessor()));
        ko.utils.toggleDomNodeCssClass(element, element[classesWrittenByBindingKey], false);
        element[classesWrittenByBindingKey] = value;
        ko.utils.toggleDomNodeCssClass(element, value, true);
    }
};

ko.bindingHandlers['css'] = {
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value !== null && typeof value == "object") {
            ko.utils.objectForEach(value, function(className, shouldHaveClass) {
                shouldHaveClass = ko.utils.unwrapObservable(shouldHaveClass);
                ko.utils.toggleDomNodeCssClass(element, className, shouldHaveClass);
            });
        } else {
            ko.bindingHandlers['class']['update'](element, valueAccessor);
        }
    }
};
ko.bindingHandlers['enable'] = {
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        if (value && element.disabled)
            element.removeAttribute("disabled");
        else if ((!value) && (!element.disabled))
            element.disabled = true;
    }
};

ko.bindingHandlers['disable'] = {
    'update': function (element, valueAccessor) {
        ko.bindingHandlers['enable']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
    }
};
// For certain common events (currently just 'click'), allow a simplified data-binding syntax
// e.g. click:handler instead of the usual full-length event:{click:handler}
function makeEventHandlerShortcut(eventName) {
    ko.bindingHandlers[eventName] = {
        'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            var newValueAccessor = function () {
                var result = {};
                result[eventName] = valueAccessor();
                return result;
            };
            return ko.bindingHandlers['event']['init'].call(this, element, newValueAccessor, allBindings, viewModel, bindingContext);
        }
    }
}

ko.bindingHandlers['event'] = {
    'init' : function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        var eventsToHandle = valueAccessor() || {};
        ko.utils.objectForEach(eventsToHandle, function(eventName) {
            if (typeof eventName == "string") {
                ko.utils.registerEventHandler(element, eventName, function (event) {
                    var handlerReturnValue;
                    var handlerFunction = valueAccessor()[eventName];
                    if (!handlerFunction)
                        return;

                    try {
                        // Take all the event args, and prefix with the viewmodel
                        var argsForHandler = ko.utils.makeArray(arguments);
                        viewModel = bindingContext['$data'];
                        argsForHandler.unshift(viewModel);
                        handlerReturnValue = handlerFunction.apply(viewModel, argsForHandler);
                    } finally {
                        if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
                            if (event.preventDefault)
                                event.preventDefault();
                            else
                                event.returnValue = false;
                        }
                    }

                    var bubble = allBindings.get(eventName + 'Bubble') !== false;
                    if (!bubble) {
                        event.cancelBubble = true;
                        if (event.stopPropagation)
                            event.stopPropagation();
                    }
                });
            }
        });
    }
};
// "foreach: someExpression" is equivalent to "template: { foreach: someExpression }"
// "foreach: { data: someExpression, afterAdd: myfn }" is equivalent to "template: { foreach: someExpression, afterAdd: myfn }"
ko.bindingHandlers['foreach'] = {
    makeTemplateValueAccessor: function(valueAccessor) {
        return function() {
            var modelValue = valueAccessor(),
                unwrappedValue = ko.utils.peekObservable(modelValue);    // Unwrap without setting a dependency here

            // If unwrappedValue is the array, pass in the wrapped value on its own
            // The value will be unwrapped and tracked within the template binding
            // (See https://github.com/SteveSanderson/knockout/issues/523)
            if ((!unwrappedValue) || typeof unwrappedValue.length == "number")
                return { 'foreach': modelValue, 'templateEngine': ko.nativeTemplateEngine.instance };

            // If unwrappedValue.data is the array, preserve all relevant options and unwrap again value so we get updates
            ko.utils.unwrapObservable(modelValue);
            return {
                'foreach': unwrappedValue['data'],
                'as': unwrappedValue['as'],
                'noChildContext': unwrappedValue['noChildContext'],
                'includeDestroyed': unwrappedValue['includeDestroyed'],
                'afterAdd': unwrappedValue['afterAdd'],
                'beforeRemove': unwrappedValue['beforeRemove'],
                'afterRender': unwrappedValue['afterRender'],
                'beforeMove': unwrappedValue['beforeMove'],
                'afterMove': unwrappedValue['afterMove'],
                'templateEngine': ko.nativeTemplateEngine.instance
            };
        };
    },
    'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        return ko.bindingHandlers['template']['init'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor));
    },
    'update': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        return ko.bindingHandlers['template']['update'](element, ko.bindingHandlers['foreach'].makeTemplateValueAccessor(valueAccessor), allBindings, viewModel, bindingContext);
    }
};
ko.expressionRewriting.bindingRewriteValidators['foreach'] = false; // Can't rewrite control flow bindings
ko.virtualElements.allowedBindings['foreach'] = true;
var hasfocusUpdatingProperty = '__ko_hasfocusUpdating';
var hasfocusLastValue = '__ko_hasfocusLastValue';
ko.bindingHandlers['hasfocus'] = {
    'init': function(element, valueAccessor, allBindings) {
        var handleElementFocusChange = function(isFocused) {
            // Where possible, ignore which event was raised and determine focus state using activeElement,
            // as this avoids phantom focus/blur events raised when changing tabs in modern browsers.
            // However, not all KO-targeted browsers (Firefox 2) support activeElement. For those browsers,
            // prevent a loss of focus when changing tabs/windows by setting a flag that prevents hasfocus
            // from calling 'blur()' on the element when it loses focus.
            // Discussion at https://github.com/SteveSanderson/knockout/pull/352
            element[hasfocusUpdatingProperty] = true;
            var ownerDoc = element.ownerDocument;
            if ("activeElement" in ownerDoc) {
                var active;
                try {
                    active = ownerDoc.activeElement;
                } catch(e) {
                    // IE9 throws if you access activeElement during page load (see issue #703)
                    active = ownerDoc.body;
                }
                isFocused = (active === element);
            }
            var modelValue = valueAccessor();
            ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'hasfocus', isFocused, true);

            //cache the latest value, so we can avoid unnecessarily calling focus/blur in the update function
            element[hasfocusLastValue] = isFocused;
            element[hasfocusUpdatingProperty] = false;
        };
        var handleElementFocusIn = handleElementFocusChange.bind(null, true);
        var handleElementFocusOut = handleElementFocusChange.bind(null, false);

        ko.utils.registerEventHandler(element, "focus", handleElementFocusIn);
        ko.utils.registerEventHandler(element, "focusin", handleElementFocusIn); // For IE
        ko.utils.registerEventHandler(element, "blur",  handleElementFocusOut);
        ko.utils.registerEventHandler(element, "focusout",  handleElementFocusOut); // For IE

        // Assume element is not focused (prevents "blur" being called initially)
        element[hasfocusLastValue] = false;
    },
    'update': function(element, valueAccessor) {
        var value = !!ko.utils.unwrapObservable(valueAccessor());

        if (!element[hasfocusUpdatingProperty] && element[hasfocusLastValue] !== value) {
            value ? element.focus() : element.blur();

            // In IE, the blur method doesn't always cause the element to lose focus (for example, if the window is not in focus).
            // Setting focus to the body element does seem to be reliable in IE, but should only be used if we know that the current
            // element was focused already.
            if (!value && element[hasfocusLastValue]) {
                element.ownerDocument.body.focus();
            }

            // For IE, which doesn't reliably fire "focus" or "blur" events synchronously
            ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, value ? "focusin" : "focusout"]);
        }
    }
};
ko.expressionRewriting.twoWayBindings['hasfocus'] = true;

ko.bindingHandlers['hasFocus'] = ko.bindingHandlers['hasfocus']; // Make "hasFocus" an alias
ko.expressionRewriting.twoWayBindings['hasFocus'] = 'hasfocus';
ko.bindingHandlers['html'] = {
    'init': function() {
        // Prevent binding on the dynamically-injected HTML (as developers are unlikely to expect that, and it has security implications)
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor) {
        // setHtml will unwrap the value if needed
        ko.utils.setHtml(element, valueAccessor());
    }
};
(function () {

// Makes a binding like with or if
function makeWithIfBinding(bindingKey, isWith, isNot) {
    ko.bindingHandlers[bindingKey] = {
        'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
            var didDisplayOnLastUpdate, savedNodes, contextOptions = {}, completeOnRender, needAsyncContext, renderOnEveryChange;

            if (isWith) {
                var as = allBindings.get('as'), noChildContext = allBindings.get('noChildContext');
                renderOnEveryChange = !(as && noChildContext);
                contextOptions = { 'as': as, 'noChildContext': noChildContext, 'exportDependencies': renderOnEveryChange };
            }

            completeOnRender = allBindings.get("completeOn") == "render";
            needAsyncContext = completeOnRender || allBindings['has'](ko.bindingEvent.descendantsComplete);

            ko.computed(function() {
                var value = ko.utils.unwrapObservable(valueAccessor()),
                    shouldDisplay = !isNot !== !value, // equivalent to isNot ? !value : !!value,
                    isInitial = !savedNodes,
                    childContext;

                if (!renderOnEveryChange && shouldDisplay === didDisplayOnLastUpdate) {
                    return;
                }

                if (needAsyncContext) {
                    bindingContext = ko.bindingEvent.startPossiblyAsyncContentBinding(element, bindingContext);
                }

                if (shouldDisplay) {
                    if (!isWith || renderOnEveryChange) {
                        contextOptions['dataDependency'] = ko.computedContext.computed();
                    }

                    if (isWith) {
                        childContext = bindingContext['createChildContext'](typeof value == "function" ? value : valueAccessor, contextOptions);
                    } else if (ko.computedContext.getDependenciesCount()) {
                        childContext = bindingContext['extend'](null, contextOptions);
                    } else {
                        childContext = bindingContext;
                    }
                }

                // Save a copy of the inner nodes on the initial update, but only if we have dependencies.
                if (isInitial && ko.computedContext.getDependenciesCount()) {
                    savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
                }

                if (shouldDisplay) {
                    if (!isInitial) {
                        ko.virtualElements.setDomNodeChildren(element, ko.utils.cloneNodes(savedNodes));
                    }

                    ko.applyBindingsToDescendants(childContext, element);
                } else {
                    ko.virtualElements.emptyNode(element);

                    if (!completeOnRender) {
                        ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
                    }
                }

                didDisplayOnLastUpdate = shouldDisplay;

            }, null, { disposeWhenNodeIsRemoved: element });

            return { 'controlsDescendantBindings': true };
        }
    };
    ko.expressionRewriting.bindingRewriteValidators[bindingKey] = false; // Can't rewrite control flow bindings
    ko.virtualElements.allowedBindings[bindingKey] = true;
}

// Construct the actual binding handlers
makeWithIfBinding('if');
makeWithIfBinding('ifnot', false /* isWith */, true /* isNot */);
makeWithIfBinding('with', true /* isWith */);

})();ko.bindingHandlers['let'] = {
    'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        // Make a modified binding context, with extra properties, and apply it to descendant elements
        var innerContext = bindingContext['extend'](valueAccessor);
        ko.applyBindingsToDescendants(innerContext, element);

        return { 'controlsDescendantBindings': true };
    }
};
ko.virtualElements.allowedBindings['let'] = true;
var captionPlaceholder = {};
ko.bindingHandlers['options'] = {
    'init': function(element) {
        if (ko.utils.tagNameLower(element) !== "select")
            throw new Error("options binding applies only to SELECT elements");

        // Remove all existing <option>s.
        while (element.length > 0) {
            element.remove(0);
        }

        // Ensures that the binding processor doesn't try to bind the options
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor, allBindings) {
        function selectedOptions() {
            return ko.utils.arrayFilter(element.options, function (node) { return node.selected; });
        }

        var selectWasPreviouslyEmpty = element.length == 0,
            multiple = element.multiple,
            previousScrollTop = (!selectWasPreviouslyEmpty && multiple) ? element.scrollTop : null,
            unwrappedArray = ko.utils.unwrapObservable(valueAccessor()),
            valueAllowUnset = allBindings.get('valueAllowUnset') && allBindings['has']('value'),
            includeDestroyed = allBindings.get('optionsIncludeDestroyed'),
            arrayToDomNodeChildrenOptions = {},
            captionValue,
            filteredArray,
            previousSelectedValues = [];

        if (!valueAllowUnset) {
            if (multiple) {
                previousSelectedValues = ko.utils.arrayMap(selectedOptions(), ko.selectExtensions.readValue);
            } else if (element.selectedIndex >= 0) {
                previousSelectedValues.push(ko.selectExtensions.readValue(element.options[element.selectedIndex]));
            }
        }

        if (unwrappedArray) {
            if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
                unwrappedArray = [unwrappedArray];

            // Filter out any entries marked as destroyed
            filteredArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
                return includeDestroyed || item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
            });

            // If caption is included, add it to the array
            if (allBindings['has']('optionsCaption')) {
                captionValue = ko.utils.unwrapObservable(allBindings.get('optionsCaption'));
                // If caption value is null or undefined, don't show a caption
                if (captionValue !== null && captionValue !== undefined) {
                    filteredArray.unshift(captionPlaceholder);
                }
            }
        } else {
            // If a falsy value is provided (e.g. null), we'll simply empty the select element
        }

        function applyToObject(object, predicate, defaultValue) {
            var predicateType = typeof predicate;
            if (predicateType == "function")    // Given a function; run it against the data value
                return predicate(object);
            else if (predicateType == "string") // Given a string; treat it as a property name on the data value
                return object[predicate];
            else                                // Given no optionsText arg; use the data value itself
                return defaultValue;
        }

        // The following functions can run at two different times:
        // The first is when the whole array is being updated directly from this binding handler.
        // The second is when an observable value for a specific array entry is updated.
        // oldOptions will be empty in the first case, but will be filled with the previously generated option in the second.
        var itemUpdate = false;
        function optionForArrayItem(arrayEntry, index, oldOptions) {
            if (oldOptions.length) {
                previousSelectedValues = !valueAllowUnset && oldOptions[0].selected ? [ ko.selectExtensions.readValue(oldOptions[0]) ] : [];
                itemUpdate = true;
            }
            var option = element.ownerDocument.createElement("option");
            if (arrayEntry === captionPlaceholder) {
                ko.utils.setTextContent(option, allBindings.get('optionsCaption'));
                ko.selectExtensions.writeValue(option, undefined);
            } else {
                // Apply a value to the option element
                var optionValue = applyToObject(arrayEntry, allBindings.get('optionsValue'), arrayEntry);
                ko.selectExtensions.writeValue(option, ko.utils.unwrapObservable(optionValue));

                // Apply some text to the option element
                var optionText = applyToObject(arrayEntry, allBindings.get('optionsText'), optionValue);
                ko.utils.setTextContent(option, optionText);
            }
            return [option];
        }

        // By using a beforeRemove callback, we delay the removal until after new items are added. This fixes a selection
        // problem in IE<=8 and Firefox. See https://github.com/knockout/knockout/issues/1208
        arrayToDomNodeChildrenOptions['beforeRemove'] =
            function (option) {
                element.removeChild(option);
            };

        function setSelectionCallback(arrayEntry, newOptions) {
            if (itemUpdate && valueAllowUnset) {
                // The model value is authoritative, so make sure its value is the one selected
                ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
            } else if (previousSelectedValues.length) {
                // IE6 doesn't like us to assign selection to OPTION nodes before they're added to the document.
                // That's why we first added them without selection. Now it's time to set the selection.
                var isSelected = ko.utils.arrayIndexOf(previousSelectedValues, ko.selectExtensions.readValue(newOptions[0])) >= 0;
                ko.utils.setOptionNodeSelectionState(newOptions[0], isSelected);

                // If this option was changed from being selected during a single-item update, notify the change
                if (itemUpdate && !isSelected) {
                    ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
                }
            }
        }

        var callback = setSelectionCallback;
        if (allBindings['has']('optionsAfterRender') && typeof allBindings.get('optionsAfterRender') == "function") {
            callback = function(arrayEntry, newOptions) {
                setSelectionCallback(arrayEntry, newOptions);
                ko.dependencyDetection.ignore(allBindings.get('optionsAfterRender'), null, [newOptions[0], arrayEntry !== captionPlaceholder ? arrayEntry : undefined]);
            }
        }

        ko.utils.setDomNodeChildrenFromArrayMapping(element, filteredArray, optionForArrayItem, arrayToDomNodeChildrenOptions, callback);

        if (!valueAllowUnset) {
            // Determine if the selection has changed as a result of updating the options list
            var selectionChanged;
            if (multiple) {
                // For a multiple-select box, compare the new selection count to the previous one
                // But if nothing was selected before, the selection can't have changed
                selectionChanged = previousSelectedValues.length && selectedOptions().length < previousSelectedValues.length;
            } else {
                // For a single-select box, compare the current value to the previous value
                // But if nothing was selected before or nothing is selected now, just look for a change in selection
                selectionChanged = (previousSelectedValues.length && element.selectedIndex >= 0)
                    ? (ko.selectExtensions.readValue(element.options[element.selectedIndex]) !== previousSelectedValues[0])
                    : (previousSelectedValues.length || element.selectedIndex >= 0);
            }

            // Ensure consistency between model value and selected option.
            // If the dropdown was changed so that selection is no longer the same,
            // notify the value or selectedOptions binding.
            if (selectionChanged) {
                ko.dependencyDetection.ignore(ko.utils.triggerEvent, null, [element, "change"]);
            }
        }

        if (valueAllowUnset || ko.computedContext.isInitial()) {
            ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
        }

        // Workaround for IE bug
        ko.utils.ensureSelectElementIsRenderedCorrectly(element);

        if (previousScrollTop && Math.abs(previousScrollTop - element.scrollTop) > 20)
            element.scrollTop = previousScrollTop;
    }
};
ko.bindingHandlers['options'].optionValueDomDataKey = ko.utils.domData.nextKey();
ko.bindingHandlers['selectedOptions'] = {
    'init': function (element, valueAccessor, allBindings) {
        function updateFromView() {
            var value = valueAccessor(), valueToWrite = [];
            ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
                if (node.selected)
                    valueToWrite.push(ko.selectExtensions.readValue(node));
            });
            ko.expressionRewriting.writeValueToProperty(value, allBindings, 'selectedOptions', valueToWrite);
        }

        function updateFromModel() {
            var newValue = ko.utils.unwrapObservable(valueAccessor()),
                previousScrollTop = element.scrollTop;

            if (newValue && typeof newValue.length == "number") {
                ko.utils.arrayForEach(element.getElementsByTagName("option"), function(node) {
                    var isSelected = ko.utils.arrayIndexOf(newValue, ko.selectExtensions.readValue(node)) >= 0;
                    if (node.selected != isSelected) {      // This check prevents flashing of the select element in IE
                        ko.utils.setOptionNodeSelectionState(node, isSelected);
                    }
                });
            }

            element.scrollTop = previousScrollTop;
        }

        if (ko.utils.tagNameLower(element) != "select") {
            throw new Error("selectedOptions binding applies only to SELECT elements");
        }

        var updateFromModelComputed;
        ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
            if (!updateFromModelComputed) {
                ko.utils.registerEventHandler(element, "change", updateFromView);
                updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
            } else {
                updateFromView();
            }
        }, null, { 'notifyImmediately': true });
    },
    'update': function() {} // Keep for backwards compatibility with code that may have wrapped binding
};
ko.expressionRewriting.twoWayBindings['selectedOptions'] = true;
ko.bindingHandlers['style'] = {
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor() || {});
        ko.utils.objectForEach(value, function(styleName, styleValue) {
            styleValue = ko.utils.unwrapObservable(styleValue);

            if (styleValue === null || styleValue === undefined || styleValue === false) {
                // Empty string removes the value, whereas null/undefined have no effect
                styleValue = "";
            }

            if (jQueryInstance) {
                jQueryInstance(element)['css'](styleName, styleValue);
            } else if (/^--/.test(styleName)) {
                // Is styleName a custom CSS property?
                element.style.setProperty(styleName, styleValue);
            } else {
                styleName = styleName.replace(/-(\w)/g, function (all, letter) {
                    return letter.toUpperCase();
                });

                var previousStyle = element.style[styleName];
                element.style[styleName] = styleValue;

                if (styleValue !== previousStyle && element.style[styleName] == previousStyle && !isNaN(styleValue)) {
                    element.style[styleName] = styleValue + "px";
                }
            }
        });
    }
};
ko.bindingHandlers['submit'] = {
    'init': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        if (typeof valueAccessor() != "function")
            throw new Error("The value for a submit binding must be a function");
        ko.utils.registerEventHandler(element, "submit", function (event) {
            var handlerReturnValue;
            var value = valueAccessor();
            try { handlerReturnValue = value.call(bindingContext['$data'], element); }
            finally {
                if (handlerReturnValue !== true) { // Normally we want to prevent default action. Developer can override this be explicitly returning true.
                    if (event.preventDefault)
                        event.preventDefault();
                    else
                        event.returnValue = false;
                }
            }
        });
    }
};
ko.bindingHandlers['text'] = {
    'init': function() {
        // Prevent binding on the dynamically-injected text node (as developers are unlikely to expect that, and it has security implications).
        // It should also make things faster, as we no longer have to consider whether the text node might be bindable.
        return { 'controlsDescendantBindings': true };
    },
    'update': function (element, valueAccessor) {
        ko.utils.setTextContent(element, valueAccessor());
    }
};
ko.virtualElements.allowedBindings['text'] = true;
(function () {

if (window && window.navigator) {
    var parseVersion = function (matches) {
        if (matches) {
            return parseFloat(matches[1]);
        }
    };

    // Detect various browser versions because some old versions don't fully support the 'input' event
    var userAgent = window.navigator.userAgent,
        operaVersion, chromeVersion, safariVersion, firefoxVersion, ieVersion, edgeVersion;

    (operaVersion = window.opera && window.opera.version && parseInt(window.opera.version()))
        || (edgeVersion = parseVersion(userAgent.match(/Edge\/([^ ]+)$/)))
        || (chromeVersion = parseVersion(userAgent.match(/Chrome\/([^ ]+)/)))
        || (safariVersion = parseVersion(userAgent.match(/Version\/([^ ]+) Safari/)))
        || (firefoxVersion = parseVersion(userAgent.match(/Firefox\/([^ ]+)/)))
        || (ieVersion = ko.utils.ieVersion || parseVersion(userAgent.match(/MSIE ([^ ]+)/)))      // Detects up to IE 10
        || (ieVersion = parseVersion(userAgent.match(/rv:([^ )]+)/)));      // Detects IE 11
}

// IE 8 and 9 have bugs that prevent the normal events from firing when the value changes.
// But it does fire the 'selectionchange' event on many of those, presumably because the
// cursor is moving and that counts as the selection changing. The 'selectionchange' event is
// fired at the document level only and doesn't directly indicate which element changed. We
// set up just one event handler for the document and use 'activeElement' to determine which
// element was changed.
if (ieVersion >= 8 && ieVersion < 10) {
    var selectionChangeRegisteredName = ko.utils.domData.nextKey(),
        selectionChangeHandlerName = ko.utils.domData.nextKey();
    var selectionChangeHandler = function(event) {
        var target = this.activeElement,
            handler = target && ko.utils.domData.get(target, selectionChangeHandlerName);
        if (handler) {
            handler(event);
        }
    };
    var registerForSelectionChangeEvent = function (element, handler) {
        var ownerDoc = element.ownerDocument;
        if (!ko.utils.domData.get(ownerDoc, selectionChangeRegisteredName)) {
            ko.utils.domData.set(ownerDoc, selectionChangeRegisteredName, true);
            ko.utils.registerEventHandler(ownerDoc, 'selectionchange', selectionChangeHandler);
        }
        ko.utils.domData.set(element, selectionChangeHandlerName, handler);
    };
}

ko.bindingHandlers['textInput'] = {
    'init': function (element, valueAccessor, allBindings) {

        var previousElementValue = element.value,
            timeoutHandle,
            elementValueBeforeEvent;

        var updateModel = function (event) {
            clearTimeout(timeoutHandle);
            elementValueBeforeEvent = timeoutHandle = undefined;

            var elementValue = element.value;
            if (previousElementValue !== elementValue) {
                // Provide a way for tests to know exactly which event was processed
                if (DEBUG && event) element['_ko_textInputProcessedEvent'] = event.type;
                previousElementValue = elementValue;
                ko.expressionRewriting.writeValueToProperty(valueAccessor(), allBindings, 'textInput', elementValue);
            }
        };

        var deferUpdateModel = function (event) {
            if (!timeoutHandle) {
                // The elementValueBeforeEvent variable is set *only* during the brief gap between an
                // event firing and the updateModel function running. This allows us to ignore model
                // updates that are from the previous state of the element, usually due to techniques
                // such as rateLimit. Such updates, if not ignored, can cause keystrokes to be lost.
                elementValueBeforeEvent = element.value;
                var handler = DEBUG ? updateModel.bind(element, {type: event.type}) : updateModel;
                timeoutHandle = ko.utils.setTimeout(handler, 4);
            }
        };

        // IE9 will mess up the DOM if you handle events synchronously which results in DOM changes (such as other bindings);
        // so we'll make sure all updates are asynchronous
        var ieUpdateModel = ko.utils.ieVersion == 9 ? deferUpdateModel : updateModel,
            ourUpdate = false;

        var updateView = function () {
            var modelValue = ko.utils.unwrapObservable(valueAccessor());

            if (modelValue === null || modelValue === undefined) {
                modelValue = '';
            }

            if (elementValueBeforeEvent !== undefined && modelValue === elementValueBeforeEvent) {
                ko.utils.setTimeout(updateView, 4);
                return;
            }

            // Update the element only if the element and model are different. On some browsers, updating the value
            // will move the cursor to the end of the input, which would be bad while the user is typing.
            if (element.value !== modelValue) {
                ourUpdate = true;  // Make sure we ignore events (propertychange) that result from updating the value
                element.value = modelValue;
                ourUpdate = false;
                previousElementValue = element.value; // In case the browser changes the value (see #2281)
            }
        };

        var onEvent = function (event, handler) {
            ko.utils.registerEventHandler(element, event, handler);
        };

        if (DEBUG && ko.bindingHandlers['textInput']['_forceUpdateOn']) {
            // Provide a way for tests to specify exactly which events are bound
            ko.utils.arrayForEach(ko.bindingHandlers['textInput']['_forceUpdateOn'], function(eventName) {
                if (eventName.slice(0,5) == 'after') {
                    onEvent(eventName.slice(5), deferUpdateModel);
                } else {
                    onEvent(eventName, updateModel);
                }
            });
        } else {
            if (ieVersion) {
                // All versions (including 11) of Internet Explorer have a bug that they don't generate an input or propertychange event when ESC is pressed
                onEvent('keypress', updateModel);
            }
            if (ieVersion < 11) {
                // Internet Explorer <= 8 doesn't support the 'input' event, but does include 'propertychange' that fires whenever
                // any property of an element changes. Unlike 'input', it also fires if a property is changed from JavaScript code,
                // but that's an acceptable compromise for this binding. IE 9 and 10 support 'input', but since they don't always
                // fire it when using autocomplete, we'll use 'propertychange' for them also.
                onEvent('propertychange', function(event) {
                    if (!ourUpdate && event.propertyName === 'value') {
                        ieUpdateModel(event);
                    }
                });
            }
            if (ieVersion == 8) {
                // IE 8 has a bug where it fails to fire 'propertychange' on the first update following a value change from
                // JavaScript code. It also doesn't fire if you clear the entire value. To fix this, we bind to the following
                // events too.
                onEvent('keyup', updateModel);      // A single keystoke
                onEvent('keydown', updateModel);    // The first character when a key is held down
            }
            if (registerForSelectionChangeEvent) {
                // Internet Explorer 9 doesn't fire the 'input' event when deleting text, including using
                // the backspace, delete, or ctrl-x keys, clicking the 'x' to clear the input, dragging text
                // out of the field, and cutting or deleting text using the context menu. 'selectionchange'
                // can detect all of those except dragging text out of the field, for which we use 'dragend'.
                // These are also needed in IE8 because of the bug described above.
                registerForSelectionChangeEvent(element, ieUpdateModel);  // 'selectionchange' covers cut, paste, drop, delete, etc.
                onEvent('dragend', deferUpdateModel);
            }

            if (!ieVersion || ieVersion >= 9) {
                // All other supported browsers support the 'input' event, which fires whenever the content of the element is changed
                // through the user interface.
                onEvent('input', ieUpdateModel);
            }

            if (safariVersion < 5 && ko.utils.tagNameLower(element) === "textarea") {
                // Safari <5 doesn't fire the 'input' event for <textarea> elements (it does fire 'textInput'
                // but only when typing). So we'll just catch as much as we can with keydown, cut, and paste.
                onEvent('keydown', deferUpdateModel);
                onEvent('paste', deferUpdateModel);
                onEvent('cut', deferUpdateModel);
            } else if (operaVersion < 11) {
                // Opera 10 doesn't always fire the 'input' event for cut, paste, undo & drop operations.
                // We can try to catch some of those using 'keydown'.
                onEvent('keydown', deferUpdateModel);
            } else if (firefoxVersion < 4.0) {
                // Firefox <= 3.6 doesn't fire the 'input' event when text is filled in through autocomplete
                onEvent('DOMAutoComplete', updateModel);

                // Firefox <=3.5 doesn't fire the 'input' event when text is dropped into the input.
                onEvent('dragdrop', updateModel);       // <3.5
                onEvent('drop', updateModel);           // 3.5
            } else if (edgeVersion && element.type === "number") {
                // Microsoft Edge doesn't fire 'input' or 'change' events for number inputs when
                // the value is changed via the up / down arrow keys
                onEvent('keydown', deferUpdateModel);
            }
        }

        // Bind to the change event so that we can catch programmatic updates of the value that fire this event.
        onEvent('change', updateModel);

        // To deal with browsers that don't notify any kind of event for some changes (IE, Safari, etc.)
        onEvent('blur', updateModel);

        ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
    }
};
ko.expressionRewriting.twoWayBindings['textInput'] = true;

// textinput is an alias for textInput
ko.bindingHandlers['textinput'] = {
    // preprocess is the only way to set up a full alias
    'preprocess': function (value, name, addBinding) {
        addBinding('textInput', value);
    }
};

})();ko.bindingHandlers['uniqueName'] = {
    'init': function (element, valueAccessor) {
        if (valueAccessor()) {
            var name = "ko_unique_" + (++ko.bindingHandlers['uniqueName'].currentIndex);
            ko.utils.setElementName(element, name);
        }
    }
};
ko.bindingHandlers['uniqueName'].currentIndex = 0;
ko.bindingHandlers['using'] = {
    'init': function(element, valueAccessor, allBindings, viewModel, bindingContext) {
        var options;

        if (allBindings['has']('as')) {
            options = { 'as': allBindings.get('as'), 'noChildContext': allBindings.get('noChildContext') };
        }

        var innerContext = bindingContext['createChildContext'](valueAccessor, options);
        ko.applyBindingsToDescendants(innerContext, element);

        return { 'controlsDescendantBindings': true };
    }
};
ko.virtualElements.allowedBindings['using'] = true;
ko.bindingHandlers['value'] = {
    'init': function (element, valueAccessor, allBindings) {
        var tagName = ko.utils.tagNameLower(element),
            isInputElement = tagName == "input";

        // If the value binding is placed on a radio/checkbox, then just pass through to checkedValue and quit
        if (isInputElement && (element.type == "checkbox" || element.type == "radio")) {
            ko.applyBindingAccessorsToNode(element, { 'checkedValue': valueAccessor });
            return;
        }

        var eventsToCatch = [];
        var requestedEventsToCatch = allBindings.get("valueUpdate");
        var propertyChangedFired = false;
        var elementValueBeforeEvent = null;

        if (requestedEventsToCatch) {
            // Allow both individual event names, and arrays of event names
            if (typeof requestedEventsToCatch == "string") {
                eventsToCatch = [requestedEventsToCatch];
            } else {
                eventsToCatch = ko.utils.arrayGetDistinctValues(requestedEventsToCatch);
            }
            ko.utils.arrayRemoveItem(eventsToCatch, "change");  // We'll subscribe to "change" events later
        }

        var valueUpdateHandler = function() {
            elementValueBeforeEvent = null;
            propertyChangedFired = false;
            var modelValue = valueAccessor();
            var elementValue = ko.selectExtensions.readValue(element);
            ko.expressionRewriting.writeValueToProperty(modelValue, allBindings, 'value', elementValue);
        }

        // Workaround for https://github.com/SteveSanderson/knockout/issues/122
        // IE doesn't fire "change" events on textboxes if the user selects a value from its autocomplete list
        var ieAutoCompleteHackNeeded = ko.utils.ieVersion && isInputElement && element.type == "text"
                                       && element.autocomplete != "off" && (!element.form || element.form.autocomplete != "off");
        if (ieAutoCompleteHackNeeded && ko.utils.arrayIndexOf(eventsToCatch, "propertychange") == -1) {
            ko.utils.registerEventHandler(element, "propertychange", function () { propertyChangedFired = true });
            ko.utils.registerEventHandler(element, "focus", function () { propertyChangedFired = false });
            ko.utils.registerEventHandler(element, "blur", function() {
                if (propertyChangedFired) {
                    valueUpdateHandler();
                }
            });
        }

        ko.utils.arrayForEach(eventsToCatch, function(eventName) {
            // The syntax "after<eventname>" means "run the handler asynchronously after the event"
            // This is useful, for example, to catch "keydown" events after the browser has updated the control
            // (otherwise, ko.selectExtensions.readValue(this) will receive the control's value *before* the key event)
            var handler = valueUpdateHandler;
            if (ko.utils.stringStartsWith(eventName, "after")) {
                handler = function() {
                    // The elementValueBeforeEvent variable is non-null *only* during the brief gap between
                    // a keyX event firing and the valueUpdateHandler running, which is scheduled to happen
                    // at the earliest asynchronous opportunity. We store this temporary information so that
                    // if, between keyX and valueUpdateHandler, the underlying model value changes separately,
                    // we can overwrite that model value change with the value the user just typed. Otherwise,
                    // techniques like rateLimit can trigger model changes at critical moments that will
                    // override the user's inputs, causing keystrokes to be lost.
                    elementValueBeforeEvent = ko.selectExtensions.readValue(element);
                    ko.utils.setTimeout(valueUpdateHandler, 0);
                };
                eventName = eventName.substring("after".length);
            }
            ko.utils.registerEventHandler(element, eventName, handler);
        });

        var updateFromModel;

        if (isInputElement && element.type == "file") {
            // For file input elements, can only write the empty string
            updateFromModel = function () {
                var newValue = ko.utils.unwrapObservable(valueAccessor());
                if (newValue === null || newValue === undefined || newValue === "") {
                    element.value = "";
                } else {
                    ko.dependencyDetection.ignore(valueUpdateHandler);  // reset the model to match the element
                }
            }
        } else {
            updateFromModel = function () {
                var newValue = ko.utils.unwrapObservable(valueAccessor());
                var elementValue = ko.selectExtensions.readValue(element);

                if (elementValueBeforeEvent !== null && newValue === elementValueBeforeEvent) {
                    ko.utils.setTimeout(updateFromModel, 0);
                    return;
                }

                var valueHasChanged = newValue !== elementValue;

                if (valueHasChanged || elementValue === undefined) {
                    if (tagName === "select") {
                        var allowUnset = allBindings.get('valueAllowUnset');
                        ko.selectExtensions.writeValue(element, newValue, allowUnset);
                        if (!allowUnset && newValue !== ko.selectExtensions.readValue(element)) {
                            // If you try to set a model value that can't be represented in an already-populated dropdown, reject that change,
                            // because you're not allowed to have a model value that disagrees with a visible UI selection.
                            ko.dependencyDetection.ignore(valueUpdateHandler);
                        }
                    } else {
                        ko.selectExtensions.writeValue(element, newValue);
                    }
                }
            };
        }

        if (tagName === "select") {
            var updateFromModelComputed;
            ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, function () {
                if (!updateFromModelComputed) {
                    ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
                    updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
                } else if (allBindings.get('valueAllowUnset')) {
                    updateFromModel();
                } else {
                    valueUpdateHandler();
                }
            }, null, { 'notifyImmediately': true });
        } else {
            ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
            ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
        }
    },
    'update': function() {} // Keep for backwards compatibility with code that may have wrapped value binding
};
ko.expressionRewriting.twoWayBindings['value'] = true;
ko.bindingHandlers['visible'] = {
    'update': function (element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        var isCurrentlyVisible = !(element.style.display == "none");
        if (value && !isCurrentlyVisible)
            element.style.display = "";
        else if ((!value) && isCurrentlyVisible)
            element.style.display = "none";
    }
};

ko.bindingHandlers['hidden'] = {
    'update': function (element, valueAccessor) {
        ko.bindingHandlers['visible']['update'](element, function() { return !ko.utils.unwrapObservable(valueAccessor()) });
    }
};
// 'click' is just a shorthand for the usual full-length event:{click:handler}
makeEventHandlerShortcut('click');
// If you want to make a custom template engine,
//
// [1] Inherit from this class (like ko.nativeTemplateEngine does)
// [2] Override 'renderTemplateSource', supplying a function with this signature:
//
//        function (templateSource, bindingContext, options) {
//            // - templateSource.text() is the text of the template you should render
//            // - bindingContext.$data is the data you should pass into the template
//            //   - you might also want to make bindingContext.$parent, bindingContext.$parents,
//            //     and bindingContext.$root available in the template too
//            // - options gives you access to any other properties set on "data-bind: { template: options }"
//            // - templateDocument is the document object of the template
//            //
//            // Return value: an array of DOM nodes
//        }
//
// [3] Override 'createJavaScriptEvaluatorBlock', supplying a function with this signature:
//
//        function (script) {
//            // Return value: Whatever syntax means "Evaluate the JavaScript statement 'script' and output the result"
//            //               For example, the jquery.tmpl template engine converts 'someScript' to '${ someScript }'
//        }
//
//     This is only necessary if you want to allow data-bind attributes to reference arbitrary template variables.
//     If you don't want to allow that, you can set the property 'allowTemplateRewriting' to false (like ko.nativeTemplateEngine does)
//     and then you don't need to override 'createJavaScriptEvaluatorBlock'.

ko.templateEngine = function () { };

ko.templateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
    throw new Error("Override renderTemplateSource");
};

ko.templateEngine.prototype['createJavaScriptEvaluatorBlock'] = function (script) {
    throw new Error("Override createJavaScriptEvaluatorBlock");
};

ko.templateEngine.prototype['makeTemplateSource'] = function(template, templateDocument) {
    // Named template
    if (typeof template == "string") {
        templateDocument = templateDocument || document;
        var elem = templateDocument.getElementById(template);
        if (!elem)
            throw new Error("Cannot find template with ID " + template);
        return new ko.templateSources.domElement(elem);
    } else if ((template.nodeType == 1) || (template.nodeType == 8)) {
        // Anonymous template
        return new ko.templateSources.anonymousTemplate(template);
    } else
        throw new Error("Unknown template type: " + template);
};

ko.templateEngine.prototype['renderTemplate'] = function (template, bindingContext, options, templateDocument) {
    var templateSource = this['makeTemplateSource'](template, templateDocument);
    return this['renderTemplateSource'](templateSource, bindingContext, options, templateDocument);
};

ko.templateEngine.prototype['isTemplateRewritten'] = function (template, templateDocument) {
    // Skip rewriting if requested
    if (this['allowTemplateRewriting'] === false)
        return true;
    return this['makeTemplateSource'](template, templateDocument)['data']("isRewritten");
};

ko.templateEngine.prototype['rewriteTemplate'] = function (template, rewriterCallback, templateDocument) {
    var templateSource = this['makeTemplateSource'](template, templateDocument);
    var rewritten = rewriterCallback(templateSource['text']());
    templateSource['text'](rewritten);
    templateSource['data']("isRewritten", true);
};

ko.exportSymbol('templateEngine', ko.templateEngine);

ko.templateRewriting = (function () {
    var memoizeDataBindingAttributeSyntaxRegex = /(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi;
    var memoizeVirtualContainerBindingSyntaxRegex = /<!--\s*ko\b\s*([\s\S]*?)\s*-->/g;

    function validateDataBindValuesForRewriting(keyValueArray) {
        var allValidators = ko.expressionRewriting.bindingRewriteValidators;
        for (var i = 0; i < keyValueArray.length; i++) {
            var key = keyValueArray[i]['key'];
            if (Object.prototype.hasOwnProperty.call(allValidators, key)) {
                var validator = allValidators[key];

                if (typeof validator === "function") {
                    var possibleErrorMessage = validator(keyValueArray[i]['value']);
                    if (possibleErrorMessage)
                        throw new Error(possibleErrorMessage);
                } else if (!validator) {
                    throw new Error("This template engine does not support the '" + key + "' binding within its templates");
                }
            }
        }
    }

    function constructMemoizedTagReplacement(dataBindAttributeValue, tagToRetain, nodeName, templateEngine) {
        var dataBindKeyValueArray = ko.expressionRewriting.parseObjectLiteral(dataBindAttributeValue);
        validateDataBindValuesForRewriting(dataBindKeyValueArray);
        var rewrittenDataBindAttributeValue = ko.expressionRewriting.preProcessBindings(dataBindKeyValueArray, {'valueAccessors':true});

        // For no obvious reason, Opera fails to evaluate rewrittenDataBindAttributeValue unless it's wrapped in an additional
        // anonymous function, even though Opera's built-in debugger can evaluate it anyway. No other browser requires this
        // extra indirection.
        var applyBindingsToNextSiblingScript =
            "ko.__tr_ambtns(function($context,$element){return(function(){return{ " + rewrittenDataBindAttributeValue + " } })()},'" + nodeName.toLowerCase() + "')";
        return templateEngine['createJavaScriptEvaluatorBlock'](applyBindingsToNextSiblingScript) + tagToRetain;
    }

    return {
        ensureTemplateIsRewritten: function (template, templateEngine, templateDocument) {
            if (!templateEngine['isTemplateRewritten'](template, templateDocument))
                templateEngine['rewriteTemplate'](template, function (htmlString) {
                    return ko.templateRewriting.memoizeBindingAttributeSyntax(htmlString, templateEngine);
                }, templateDocument);
        },

        memoizeBindingAttributeSyntax: function (htmlString, templateEngine) {
            return htmlString.replace(memoizeDataBindingAttributeSyntaxRegex, function () {
                return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[4], /* tagToRetain: */ arguments[1], /* nodeName: */ arguments[2], templateEngine);
            }).replace(memoizeVirtualContainerBindingSyntaxRegex, function() {
                return constructMemoizedTagReplacement(/* dataBindAttributeValue: */ arguments[1], /* tagToRetain: */ "<!-- ko -->", /* nodeName: */ "#comment", templateEngine);
            });
        },

        applyMemoizedBindingsToNextSibling: function (bindings, nodeName) {
            return ko.memoization.memoize(function (domNode, bindingContext) {
                var nodeToBind = domNode.nextSibling;
                if (nodeToBind && nodeToBind.nodeName.toLowerCase() === nodeName) {
                    ko.applyBindingAccessorsToNode(nodeToBind, bindings, bindingContext);
                }
            });
        }
    }
})();


// Exported only because it has to be referenced by string lookup from within rewritten template
ko.exportSymbol('__tr_ambtns', ko.templateRewriting.applyMemoizedBindingsToNextSibling);
(function() {
    // A template source represents a read/write way of accessing a template. This is to eliminate the need for template loading/saving
    // logic to be duplicated in every template engine (and means they can all work with anonymous templates, etc.)
    //
    // Two are provided by default:
    //  1. ko.templateSources.domElement       - reads/writes the text content of an arbitrary DOM element
    //  2. ko.templateSources.anonymousElement - uses ko.utils.domData to read/write text *associated* with the DOM element, but
    //                                           without reading/writing the actual element text content, since it will be overwritten
    //                                           with the rendered template output.
    // You can implement your own template source if you want to fetch/store templates somewhere other than in DOM elements.
    // Template sources need to have the following functions:
    //   text() 			- returns the template text from your storage location
    //   text(value)		- writes the supplied template text to your storage location
    //   data(key)			- reads values stored using data(key, value) - see below
    //   data(key, value)	- associates "value" with this template and the key "key". Is used to store information like "isRewritten".
    //
    // Optionally, template sources can also have the following functions:
    //   nodes()            - returns a DOM element containing the nodes of this template, where available
    //   nodes(value)       - writes the given DOM element to your storage location
    // If a DOM element is available for a given template source, template engines are encouraged to use it in preference over text()
    // for improved speed. However, all templateSources must supply text() even if they don't supply nodes().
    //
    // Once you've implemented a templateSource, make your template engine use it by subclassing whatever template engine you were
    // using and overriding "makeTemplateSource" to return an instance of your custom template source.

    ko.templateSources = {};

    // ---- ko.templateSources.domElement -----

    // template types
    var templateScript = 1,
        templateTextArea = 2,
        templateTemplate = 3,
        templateElement = 4;

    ko.templateSources.domElement = function(element) {
        this.domElement = element;

        if (element) {
            var tagNameLower = ko.utils.tagNameLower(element);
            this.templateType =
                tagNameLower === "script" ? templateScript :
                tagNameLower === "textarea" ? templateTextArea :
                    // For browsers with proper <template> element support, where the .content property gives a document fragment
                tagNameLower == "template" && element.content && element.content.nodeType === 11 ? templateTemplate :
                templateElement;
        }
    }

    ko.templateSources.domElement.prototype['text'] = function(/* valueToWrite */) {
        var elemContentsProperty = this.templateType === templateScript ? "text"
                                 : this.templateType === templateTextArea ? "value"
                                 : "innerHTML";

        if (arguments.length == 0) {
            return this.domElement[elemContentsProperty];
        } else {
            var valueToWrite = arguments[0];
            if (elemContentsProperty === "innerHTML")
                ko.utils.setHtml(this.domElement, valueToWrite);
            else
                this.domElement[elemContentsProperty] = valueToWrite;
        }
    };

    var dataDomDataPrefix = ko.utils.domData.nextKey() + "_";
    ko.templateSources.domElement.prototype['data'] = function(key /*, valueToWrite */) {
        if (arguments.length === 1) {
            return ko.utils.domData.get(this.domElement, dataDomDataPrefix + key);
        } else {
            ko.utils.domData.set(this.domElement, dataDomDataPrefix + key, arguments[1]);
        }
    };

    var templatesDomDataKey = ko.utils.domData.nextKey();
    function getTemplateDomData(element) {
        return ko.utils.domData.get(element, templatesDomDataKey) || {};
    }
    function setTemplateDomData(element, data) {
        ko.utils.domData.set(element, templatesDomDataKey, data);
    }

    ko.templateSources.domElement.prototype['nodes'] = function(/* valueToWrite */) {
        var element = this.domElement;
        if (arguments.length == 0) {
            var templateData = getTemplateDomData(element),
                nodes = templateData.containerData || (
                    this.templateType === templateTemplate ? element.content :
                    this.templateType === templateElement ? element :
                    undefined);
            if (!nodes || templateData.alwaysCheckText) {
                // If the template is associated with an element that stores the template as text,
                // parse and cache the nodes whenever there's new text content available. This allows
                // the user to update the template content by updating the text of template node.
                var text = this['text']();
                if (text && text !== templateData.textData) {
                    nodes = ko.utils.parseHtmlForTemplateNodes(text, element.ownerDocument);
                    setTemplateDomData(element, {containerData: nodes, textData: text, alwaysCheckText: true});
                }
            }
            return nodes;
        } else {
            var valueToWrite = arguments[0];
            if (this.templateType !== undefined) {
                this['text']("");   // clear the text from the node
            }
            setTemplateDomData(element, {containerData: valueToWrite});
        }
    };

    // ---- ko.templateSources.anonymousTemplate -----
    // Anonymous templates are normally saved/retrieved as DOM nodes through "nodes".
    // For compatibility, you can also read "text"; it will be serialized from the nodes on demand.
    // Writing to "text" is still supported, but then the template data will not be available as DOM nodes.

    ko.templateSources.anonymousTemplate = function(element) {
        this.domElement = element;
    }
    ko.templateSources.anonymousTemplate.prototype = new ko.templateSources.domElement();
    ko.templateSources.anonymousTemplate.prototype.constructor = ko.templateSources.anonymousTemplate;
    ko.templateSources.anonymousTemplate.prototype['text'] = function(/* valueToWrite */) {
        if (arguments.length == 0) {
            var templateData = getTemplateDomData(this.domElement);
            if (templateData.textData === undefined && templateData.containerData)
                templateData.textData = templateData.containerData.innerHTML;
            return templateData.textData;
        } else {
            var valueToWrite = arguments[0];
            setTemplateDomData(this.domElement, {textData: valueToWrite});
        }
    };

    ko.exportSymbol('templateSources', ko.templateSources);
    ko.exportSymbol('templateSources.domElement', ko.templateSources.domElement);
    ko.exportSymbol('templateSources.anonymousTemplate', ko.templateSources.anonymousTemplate);
})();
(function () {
    var _templateEngine;
    ko.setTemplateEngine = function (templateEngine) {
        if ((templateEngine != undefined) && !(templateEngine instanceof ko.templateEngine))
            throw new Error("templateEngine must inherit from ko.templateEngine");
        _templateEngine = templateEngine;
    }

    function invokeForEachNodeInContinuousRange(firstNode, lastNode, action) {
        var node, nextInQueue = firstNode, firstOutOfRangeNode = ko.virtualElements.nextSibling(lastNode);
        while (nextInQueue && ((node = nextInQueue) !== firstOutOfRangeNode)) {
            nextInQueue = ko.virtualElements.nextSibling(node);
            action(node, nextInQueue);
        }
    }

    function activateBindingsOnContinuousNodeArray(continuousNodeArray, bindingContext) {
        // To be used on any nodes that have been rendered by a template and have been inserted into some parent element
        // Walks through continuousNodeArray (which *must* be continuous, i.e., an uninterrupted sequence of sibling nodes, because
        // the algorithm for walking them relies on this), and for each top-level item in the virtual-element sense,
        // (1) Does a regular "applyBindings" to associate bindingContext with this node and to activate any non-memoized bindings
        // (2) Unmemoizes any memos in the DOM subtree (e.g., to activate bindings that had been memoized during template rewriting)

        if (continuousNodeArray.length) {
            var firstNode = continuousNodeArray[0],
                lastNode = continuousNodeArray[continuousNodeArray.length - 1],
                parentNode = firstNode.parentNode,
                provider = ko.bindingProvider['instance'],
                preprocessNode = provider['preprocessNode'];

            if (preprocessNode) {
                invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node, nextNodeInRange) {
                    var nodePreviousSibling = node.previousSibling;
                    var newNodes = preprocessNode.call(provider, node);
                    if (newNodes) {
                        if (node === firstNode)
                            firstNode = newNodes[0] || nextNodeInRange;
                        if (node === lastNode)
                            lastNode = newNodes[newNodes.length - 1] || nodePreviousSibling;
                    }
                });

                // Because preprocessNode can change the nodes, including the first and last nodes, update continuousNodeArray to match.
                // We need the full set, including inner nodes, because the unmemoize step might remove the first node (and so the real
                // first node needs to be in the array).
                continuousNodeArray.length = 0;
                if (!firstNode) { // preprocessNode might have removed all the nodes, in which case there's nothing left to do
                    return;
                }
                if (firstNode === lastNode) {
                    continuousNodeArray.push(firstNode);
                } else {
                    continuousNodeArray.push(firstNode, lastNode);
                    ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);
                }
            }

            // Need to applyBindings *before* unmemoziation, because unmemoization might introduce extra nodes (that we don't want to re-bind)
            // whereas a regular applyBindings won't introduce new memoized nodes
            invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node) {
                if (node.nodeType === 1 || node.nodeType === 8)
                    ko.applyBindings(bindingContext, node);
            });
            invokeForEachNodeInContinuousRange(firstNode, lastNode, function(node) {
                if (node.nodeType === 1 || node.nodeType === 8)
                    ko.memoization.unmemoizeDomNodeAndDescendants(node, [bindingContext]);
            });

            // Make sure any changes done by applyBindings or unmemoize are reflected in the array
            ko.utils.fixUpContinuousNodeArray(continuousNodeArray, parentNode);
        }
    }

    function getFirstNodeFromPossibleArray(nodeOrNodeArray) {
        return nodeOrNodeArray.nodeType ? nodeOrNodeArray
                                        : nodeOrNodeArray.length > 0 ? nodeOrNodeArray[0]
                                        : null;
    }

    function executeTemplate(targetNodeOrNodeArray, renderMode, template, bindingContext, options) {
        options = options || {};
        var firstTargetNode = targetNodeOrNodeArray && getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
        var templateDocument = (firstTargetNode || template || {}).ownerDocument;
        var templateEngineToUse = (options['templateEngine'] || _templateEngine);
        ko.templateRewriting.ensureTemplateIsRewritten(template, templateEngineToUse, templateDocument);
        var renderedNodesArray = templateEngineToUse['renderTemplate'](template, bindingContext, options, templateDocument);

        // Loosely check result is an array of DOM nodes
        if ((typeof renderedNodesArray.length != "number") || (renderedNodesArray.length > 0 && typeof renderedNodesArray[0].nodeType != "number"))
            throw new Error("Template engine must return an array of DOM nodes");

        var haveAddedNodesToParent = false;
        switch (renderMode) {
            case "replaceChildren":
                ko.virtualElements.setDomNodeChildren(targetNodeOrNodeArray, renderedNodesArray);
                haveAddedNodesToParent = true;
                break;
            case "replaceNode":
                ko.utils.replaceDomNodes(targetNodeOrNodeArray, renderedNodesArray);
                haveAddedNodesToParent = true;
                break;
            case "ignoreTargetNode": break;
            default:
                throw new Error("Unknown renderMode: " + renderMode);
        }

        if (haveAddedNodesToParent) {
            activateBindingsOnContinuousNodeArray(renderedNodesArray, bindingContext);
            if (options['afterRender']) {
                ko.dependencyDetection.ignore(options['afterRender'], null, [renderedNodesArray, bindingContext[options['as'] || '$data']]);
            }
            if (renderMode == "replaceChildren") {
                ko.bindingEvent.notify(targetNodeOrNodeArray, ko.bindingEvent.childrenComplete);
            }
        }

        return renderedNodesArray;
    }

    function resolveTemplateName(template, data, context) {
        // The template can be specified as:
        if (ko.isObservable(template)) {
            // 1. An observable, with string value
            return template();
        } else if (typeof template === 'function') {
            // 2. A function of (data, context) returning a string
            return template(data, context);
        } else {
            // 3. A string
            return template;
        }
    }

    ko.renderTemplate = function (template, dataOrBindingContext, options, targetNodeOrNodeArray, renderMode) {
        options = options || {};
        if ((options['templateEngine'] || _templateEngine) == undefined)
            throw new Error("Set a template engine before calling renderTemplate");
        renderMode = renderMode || "replaceChildren";

        if (targetNodeOrNodeArray) {
            var firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);

            var whenToDispose = function () { return (!firstTargetNode) || !ko.utils.domNodeIsAttachedToDocument(firstTargetNode); }; // Passive disposal (on next evaluation)
            var activelyDisposeWhenNodeIsRemoved = (firstTargetNode && renderMode == "replaceNode") ? firstTargetNode.parentNode : firstTargetNode;

            return ko.dependentObservable( // So the DOM is automatically updated when any dependency changes
                function () {
                    // Ensure we've got a proper binding context to work with
                    var bindingContext = (dataOrBindingContext && (dataOrBindingContext instanceof ko.bindingContext))
                        ? dataOrBindingContext
                        : new ko.bindingContext(dataOrBindingContext, null, null, null, { "exportDependencies": true });

                    var templateName = resolveTemplateName(template, bindingContext['$data'], bindingContext),
                        renderedNodesArray = executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);

                    if (renderMode == "replaceNode") {
                        targetNodeOrNodeArray = renderedNodesArray;
                        firstTargetNode = getFirstNodeFromPossibleArray(targetNodeOrNodeArray);
                    }
                },
                null,
                { disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: activelyDisposeWhenNodeIsRemoved }
            );
        } else {
            // We don't yet have a DOM node to evaluate, so use a memo and render the template later when there is a DOM node
            return ko.memoization.memoize(function (domNode) {
                ko.renderTemplate(template, dataOrBindingContext, options, domNode, "replaceNode");
            });
        }
    };

    ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) {
        // Since setDomNodeChildrenFromArrayMapping always calls executeTemplateForArrayItem and then
        // activateBindingsCallback for added items, we can store the binding context in the former to use in the latter.
        var arrayItemContext, asName = options['as'];

        // This will be called by setDomNodeChildrenFromArrayMapping to get the nodes to add to targetNode
        var executeTemplateForArrayItem = function (arrayValue, index) {
            // Support selecting template as a function of the data being rendered
            arrayItemContext = parentBindingContext['createChildContext'](arrayValue, {
                'as': asName,
                'noChildContext': options['noChildContext'],
                'extend': function(context) {
                    context['$index'] = index;
                    if (asName) {
                        context[asName + "Index"] = index;
                    }
                }
            });

            var templateName = resolveTemplateName(template, arrayValue, arrayItemContext);
            return executeTemplate(targetNode, "ignoreTargetNode", templateName, arrayItemContext, options);
        };

        // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode
        var activateBindingsCallback = function(arrayValue, addedNodesArray, index) {
            activateBindingsOnContinuousNodeArray(addedNodesArray, arrayItemContext);
            if (options['afterRender'])
                options['afterRender'](addedNodesArray, arrayValue);

            // release the "cache" variable, so that it can be collected by
            // the GC when its value isn't used from within the bindings anymore.
            arrayItemContext = null;
        };

        var setDomNodeChildrenFromArrayMapping = function (newArray, changeList) {
            // Call setDomNodeChildrenFromArrayMapping, ignoring any observables unwrapped within (most likely from a callback function).
            // If the array items are observables, though, they will be unwrapped in executeTemplateForArrayItem and managed within setDomNodeChildrenFromArrayMapping.
            ko.dependencyDetection.ignore(ko.utils.setDomNodeChildrenFromArrayMapping, null, [targetNode, newArray, executeTemplateForArrayItem, options, activateBindingsCallback, changeList]);
            ko.bindingEvent.notify(targetNode, ko.bindingEvent.childrenComplete);
        };

        var shouldHideDestroyed = (options['includeDestroyed'] === false) || (ko.options['foreachHidesDestroyed'] && !options['includeDestroyed']);

        if (!shouldHideDestroyed && !options['beforeRemove'] && ko.isObservableArray(arrayOrObservableArray)) {
            setDomNodeChildrenFromArrayMapping(arrayOrObservableArray.peek());

            var subscription = arrayOrObservableArray.subscribe(function (changeList) {
                setDomNodeChildrenFromArrayMapping(arrayOrObservableArray(), changeList);
            }, null, "arrayChange");
            subscription.disposeWhenNodeIsRemoved(targetNode);

            return subscription;
        } else {
            return ko.dependentObservable(function () {
                var unwrappedArray = ko.utils.unwrapObservable(arrayOrObservableArray) || [];
                if (typeof unwrappedArray.length == "undefined") // Coerce single value into array
                    unwrappedArray = [unwrappedArray];

                if (shouldHideDestroyed) {
                    // Filter out any entries marked as destroyed
                    unwrappedArray = ko.utils.arrayFilter(unwrappedArray, function(item) {
                        return item === undefined || item === null || !ko.utils.unwrapObservable(item['_destroy']);
                    });
                }
                setDomNodeChildrenFromArrayMapping(unwrappedArray);

            }, null, { disposeWhenNodeIsRemoved: targetNode });
        }
    };

    var templateComputedDomDataKey = ko.utils.domData.nextKey();
    function disposeOldComputedAndStoreNewOne(element, newComputed) {
        var oldComputed = ko.utils.domData.get(element, templateComputedDomDataKey);
        if (oldComputed && (typeof(oldComputed.dispose) == 'function'))
            oldComputed.dispose();
        ko.utils.domData.set(element, templateComputedDomDataKey, (newComputed && (!newComputed.isActive || newComputed.isActive())) ? newComputed : undefined);
    }

    var cleanContainerDomDataKey = ko.utils.domData.nextKey();
    ko.bindingHandlers['template'] = {
        'init': function(element, valueAccessor) {
            // Support anonymous templates
            var bindingValue = ko.utils.unwrapObservable(valueAccessor());
            if (typeof bindingValue == "string" || 'name' in bindingValue) {
                // It's a named template - clear the element
                ko.virtualElements.emptyNode(element);
            } else if ('nodes' in bindingValue) {
                // We've been given an array of DOM nodes. Save them as the template source.
                // There is no known use case for the node array being an observable array (if the output
                // varies, put that behavior *into* your template - that's what templates are for), and
                // the implementation would be a mess, so assert that it's not observable.
                var nodes = bindingValue['nodes'] || [];
                if (ko.isObservable(nodes)) {
                    throw new Error('The "nodes" option must be a plain, non-observable array.');
                }

                // If the nodes are already attached to a KO-generated container, we reuse that container without moving the
                // elements to a new one (we check only the first node, as the nodes are always moved together)
                var container = nodes[0] && nodes[0].parentNode;
                if (!container || !ko.utils.domData.get(container, cleanContainerDomDataKey)) {
                    container = ko.utils.moveCleanedNodesToContainerElement(nodes);
                    ko.utils.domData.set(container, cleanContainerDomDataKey, true);
                }

                new ko.templateSources.anonymousTemplate(element)['nodes'](container);
            } else {
                // It's an anonymous template - store the element contents, then clear the element
                var templateNodes = ko.virtualElements.childNodes(element);
                if (templateNodes.length > 0) {
                    var container = ko.utils.moveCleanedNodesToContainerElement(templateNodes); // This also removes the nodes from their current parent
                    new ko.templateSources.anonymousTemplate(element)['nodes'](container);
                } else {
                    throw new Error("Anonymous template defined, but no template content was provided");
                }
            }
            return { 'controlsDescendantBindings': true };
        },
        'update': function (element, valueAccessor, allBindings, viewModel, bindingContext) {
            var value = valueAccessor(),
                options = ko.utils.unwrapObservable(value),
                shouldDisplay = true,
                templateComputed = null,
                template;

            if (typeof options == "string") {
                template = value;
                options = {};
            } else {
                template = 'name' in options ? options['name'] : element;

                // Support "if"/"ifnot" conditions
                if ('if' in options)
                    shouldDisplay = ko.utils.unwrapObservable(options['if']);
                if (shouldDisplay && 'ifnot' in options)
                    shouldDisplay = !ko.utils.unwrapObservable(options['ifnot']);

                // Don't show anything if an empty name is given (see #2446)
                if (shouldDisplay && !template) {
                    shouldDisplay = false;
                }
            }

            if ('foreach' in options) {
                // Render once for each data point (treating data set as empty if shouldDisplay==false)
                var dataArray = (shouldDisplay && options['foreach']) || [];
                templateComputed = ko.renderTemplateForEach(template, dataArray, options, element, bindingContext);
            } else if (!shouldDisplay) {
                ko.virtualElements.emptyNode(element);
            } else {
                // Render once for this single data point (or use the viewModel if no data was provided)
                var innerBindingContext = bindingContext;
                if ('data' in options) {
                    innerBindingContext = bindingContext['createChildContext'](options['data'], {
                        'as': options['as'],
                        'noChildContext': options['noChildContext'],
                        'exportDependencies': true
                    });
                }
                templateComputed = ko.renderTemplate(template, innerBindingContext, options, element);
            }

            // It only makes sense to have a single template computed per element (otherwise which one should have its output displayed?)
            disposeOldComputedAndStoreNewOne(element, templateComputed);
        }
    };

    // Anonymous templates can't be rewritten. Give a nice error message if you try to do it.
    ko.expressionRewriting.bindingRewriteValidators['template'] = function(bindingValue) {
        var parsedBindingValue = ko.expressionRewriting.parseObjectLiteral(bindingValue);

        if ((parsedBindingValue.length == 1) && parsedBindingValue[0]['unknown'])
            return null; // It looks like a string literal, not an object literal, so treat it as a named template (which is allowed for rewriting)

        if (ko.expressionRewriting.keyValueArrayContainsKey(parsedBindingValue, "name"))
            return null; // Named templates can be rewritten, so return "no error"
        return "This template engine does not support anonymous templates nested within its templates";
    };

    ko.virtualElements.allowedBindings['template'] = true;
})();

ko.exportSymbol('setTemplateEngine', ko.setTemplateEngine);
ko.exportSymbol('renderTemplate', ko.renderTemplate);
// Go through the items that have been added and deleted and try to find matches between them.
ko.utils.findMovesInArrayComparison = function (left, right, limitFailedCompares) {
    if (left.length && right.length) {
        var failedCompares, l, r, leftItem, rightItem;
        for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
            for (r = 0; rightItem = right[r]; ++r) {
                if (leftItem['value'] === rightItem['value']) {
                    leftItem['moved'] = rightItem['index'];
                    rightItem['moved'] = leftItem['index'];
                    right.splice(r, 1);         // This item is marked as moved; so remove it from right list
                    failedCompares = r = 0;     // Reset failed compares count because we're checking for consecutive failures
                    break;
                }
            }
            failedCompares += r;
        }
    }
};

ko.utils.compareArrays = (function () {
    var statusNotInOld = 'added', statusNotInNew = 'deleted';

    // Simple calculation based on Levenshtein distance.
    function compareArrays(oldArray, newArray, options) {
        // For backward compatibility, if the third arg is actually a bool, interpret
        // it as the old parameter 'dontLimitMoves'. Newer code should use { dontLimitMoves: true }.
        options = (typeof options === 'boolean') ? { 'dontLimitMoves': options } : (options || {});
        oldArray = oldArray || [];
        newArray = newArray || [];

        if (oldArray.length < newArray.length)
            return compareSmallArrayToBigArray(oldArray, newArray, statusNotInOld, statusNotInNew, options);
        else
            return compareSmallArrayToBigArray(newArray, oldArray, statusNotInNew, statusNotInOld, options);
    }

    function compareSmallArrayToBigArray(smlArray, bigArray, statusNotInSml, statusNotInBig, options) {
        var myMin = Math.min,
            myMax = Math.max,
            editDistanceMatrix = [],
            smlIndex, smlIndexMax = smlArray.length,
            bigIndex, bigIndexMax = bigArray.length,
            compareRange = (bigIndexMax - smlIndexMax) || 1,
            maxDistance = smlIndexMax + bigIndexMax + 1,
            thisRow, lastRow,
            bigIndexMaxForRow, bigIndexMinForRow;

        for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
            lastRow = thisRow;
            editDistanceMatrix.push(thisRow = []);
            bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
            bigIndexMinForRow = myMax(0, smlIndex - 1);
            for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
                if (!bigIndex)
                    thisRow[bigIndex] = smlIndex + 1;
                else if (!smlIndex)  // Top row - transform empty array into new array via additions
                    thisRow[bigIndex] = bigIndex + 1;
                else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
                    thisRow[bigIndex] = lastRow[bigIndex - 1];                  // copy value (no edit)
                else {
                    var northDistance = lastRow[bigIndex] || maxDistance;       // not in big (deletion)
                    var westDistance = thisRow[bigIndex - 1] || maxDistance;    // not in small (addition)
                    thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
                }
            }
        }

        var editScript = [], meMinusOne, notInSml = [], notInBig = [];
        for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
            meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
            if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
                notInSml.push(editScript[editScript.length] = {     // added
                    'status': statusNotInSml,
                    'value': bigArray[--bigIndex],
                    'index': bigIndex });
            } else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
                notInBig.push(editScript[editScript.length] = {     // deleted
                    'status': statusNotInBig,
                    'value': smlArray[--smlIndex],
                    'index': smlIndex });
            } else {
                --bigIndex;
                --smlIndex;
                if (!options['sparse']) {
                    editScript.push({
                        'status': "retained",
                        'value': bigArray[bigIndex] });
                }
            }
        }

        // Set a limit on the number of consecutive non-matching comparisons; having it a multiple of
        // smlIndexMax keeps the time complexity of this algorithm linear.
        ko.utils.findMovesInArrayComparison(notInBig, notInSml, !options['dontLimitMoves'] && smlIndexMax * 10);

        return editScript.reverse();
    }

    return compareArrays;
})();

ko.exportSymbol('utils.compareArrays', ko.utils.compareArrays);
(function () {
    // Objective:
    // * Given an input array, a container DOM node, and a function from array elements to arrays of DOM nodes,
    //   map the array elements to arrays of DOM nodes, concatenate together all these arrays, and use them to populate the container DOM node
    // * Next time we're given the same combination of things (with the array possibly having mutated), update the container DOM node
    //   so that its children is again the concatenation of the mappings of the array elements, but don't re-map any array elements that we
    //   previously mapped - retain those nodes, and just insert/delete other ones

    // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node
    // You can use this, for example, to activate bindings on those nodes.

    function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) {
        // Map this array value inside a dependentObservable so we re-map when any dependency changes
        var mappedNodes = [];
        var dependentObservable = ko.dependentObservable(function() {
            var newMappedNodes = mapping(valueToMap, index, ko.utils.fixUpContinuousNodeArray(mappedNodes, containerNode)) || [];

            // On subsequent evaluations, just replace the previously-inserted DOM nodes
            if (mappedNodes.length > 0) {
                ko.utils.replaceDomNodes(mappedNodes, newMappedNodes);
                if (callbackAfterAddingNodes)
                    ko.dependencyDetection.ignore(callbackAfterAddingNodes, null, [valueToMap, newMappedNodes, index]);
            }

            // Replace the contents of the mappedNodes array, thereby updating the record
            // of which nodes would be deleted if valueToMap was itself later removed
            mappedNodes.length = 0;
            ko.utils.arrayPushAll(mappedNodes, newMappedNodes);
        }, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: function() { return !ko.utils.anyDomNodeIsAttachedToDocument(mappedNodes); } });
        return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
    }

    var lastMappingResultDomDataKey = ko.utils.domData.nextKey(),
        deletedItemDummyValue = ko.utils.domData.nextKey();

    ko.utils.setDomNodeChildrenFromArrayMapping = function (domNode, array, mapping, options, callbackAfterAddingNodes, editScript) {
        array = array || [];
        if (typeof array.length == "undefined") // Coerce single value into array
            array = [array];

        options = options || {};
        var lastMappingResult = ko.utils.domData.get(domNode, lastMappingResultDomDataKey);
        var isFirstExecution = !lastMappingResult;

        // Build the new mapping result
        var newMappingResult = [];
        var lastMappingResultIndex = 0;
        var currentArrayIndex = 0;

        var nodesToDelete = [];
        var itemsToMoveFirstIndexes = [];
        var itemsForBeforeRemoveCallbacks = [];
        var itemsForMoveCallbacks = [];
        var itemsForAfterAddCallbacks = [];
        var mapData;
        var countWaitingForRemove = 0;

        function itemAdded(value) {
            mapData = { arrayEntry: value, indexObservable: ko.observable(currentArrayIndex++) };
            newMappingResult.push(mapData);
            if (!isFirstExecution) {
                itemsForAfterAddCallbacks.push(mapData);
            }
        }

        function itemMovedOrRetained(oldPosition) {
            mapData = lastMappingResult[oldPosition];
            if (currentArrayIndex !== mapData.indexObservable.peek())
                itemsForMoveCallbacks.push(mapData);
            // Since updating the index might change the nodes, do so before calling fixUpContinuousNodeArray
            mapData.indexObservable(currentArrayIndex++);
            ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode);
            newMappingResult.push(mapData);
        }

        function callCallback(callback, items) {
            if (callback) {
                for (var i = 0, n = items.length; i < n; i++) {
                    ko.utils.arrayForEach(items[i].mappedNodes, function(node) {
                        callback(node, i, items[i].arrayEntry);
                    });
                }
            }
        }

        if (isFirstExecution) {
            ko.utils.arrayForEach(array, itemAdded);
        } else {
            if (!editScript || (lastMappingResult && lastMappingResult['_countWaitingForRemove'])) {
                // Compare the provided array against the previous one
                var lastArray = ko.utils.arrayMap(lastMappingResult, function (x) { return x.arrayEntry; }),
                    compareOptions = {
                        'dontLimitMoves': options['dontLimitMoves'],
                        'sparse': true
                    };
                editScript = ko.utils.compareArrays(lastArray, array, compareOptions);
            }

            for (var i = 0, editScriptItem, movedIndex, itemIndex; editScriptItem = editScript[i]; i++) {
                movedIndex = editScriptItem['moved'];
                itemIndex = editScriptItem['index'];
                switch (editScriptItem['status']) {
                    case "deleted":
                        while (lastMappingResultIndex < itemIndex) {
                            itemMovedOrRetained(lastMappingResultIndex++);
                        }
                        if (movedIndex === undefined) {
                            mapData = lastMappingResult[lastMappingResultIndex];

                            // Stop tracking changes to the mapping for these nodes
                            if (mapData.dependentObservable) {
                                mapData.dependentObservable.dispose();
                                mapData.dependentObservable = undefined;
                            }

                            // Queue these nodes for later removal
                            if (ko.utils.fixUpContinuousNodeArray(mapData.mappedNodes, domNode).length) {
                                if (options['beforeRemove']) {
                                    newMappingResult.push(mapData);
                                    countWaitingForRemove++;
                                    if (mapData.arrayEntry === deletedItemDummyValue) {
                                        mapData = null;
                                    } else {
                                        itemsForBeforeRemoveCallbacks.push(mapData);
                                    }
                                }
                                if (mapData) {
                                    nodesToDelete.push.apply(nodesToDelete, mapData.mappedNodes);
                                }
                            }
                        }
                        lastMappingResultIndex++;
                        break;

                    case "added":
                        while (currentArrayIndex < itemIndex) {
                            itemMovedOrRetained(lastMappingResultIndex++);
                        }
                        if (movedIndex !== undefined) {
                            itemsToMoveFirstIndexes.push(newMappingResult.length);
                            itemMovedOrRetained(movedIndex);
                        } else {
                            itemAdded(editScriptItem['value']);
                        }
                        break;
                }
            }

            while (currentArrayIndex < array.length) {
                itemMovedOrRetained(lastMappingResultIndex++);
            }

            // Record that the current view may still contain deleted items
            // because it means we won't be able to use a provided editScript.
            newMappingResult['_countWaitingForRemove'] = countWaitingForRemove;
        }

        // Store a copy of the array items we just considered so we can difference it next time
        ko.utils.domData.set(domNode, lastMappingResultDomDataKey, newMappingResult);

        // Call beforeMove first before any changes have been made to the DOM
        callCallback(options['beforeMove'], itemsForMoveCallbacks);

        // Next remove nodes for deleted items (or just clean if there's a beforeRemove callback)
        ko.utils.arrayForEach(nodesToDelete, options['beforeRemove'] ? ko.cleanNode : ko.removeNode);

        var i, j, lastNode, nodeToInsert, mappedNodes, activeElement;

        // Since most browsers remove the focus from an element when it's moved to another location,
        // save the focused element and try to restore it later.
        try {
            activeElement = domNode.ownerDocument.activeElement;
        } catch(e) {
            // IE9 throws if you access activeElement during page load (see issue #703)
        }

        // Try to reduce overall moved nodes by first moving the ones that were marked as moved by the edit script
        if (itemsToMoveFirstIndexes.length) {
            while ((i = itemsToMoveFirstIndexes.shift()) != undefined) {
                mapData = newMappingResult[i];
                for (lastNode = undefined; i; ) {
                    if ((mappedNodes = newMappingResult[--i].mappedNodes) && mappedNodes.length) {
                        lastNode = mappedNodes[mappedNodes.length-1];
                        break;
                    }
                }
                for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {
                    ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);
                }
            }
        }

        // Next add/reorder the remaining items (will include deleted items if there's a beforeRemove callback)
        for (i = 0; mapData = newMappingResult[i]; i++) {
            // Get nodes for newly added items
            if (!mapData.mappedNodes)
                ko.utils.extend(mapData, mapNodeAndRefreshWhenChanged(domNode, mapping, mapData.arrayEntry, callbackAfterAddingNodes, mapData.indexObservable));

            // Put nodes in the right place if they aren't there already
            for (j = 0; nodeToInsert = mapData.mappedNodes[j]; lastNode = nodeToInsert, j++) {
                ko.virtualElements.insertAfter(domNode, nodeToInsert, lastNode);
            }

            // Run the callbacks for newly added nodes (for example, to apply bindings, etc.)
            if (!mapData.initialized && callbackAfterAddingNodes) {
                callbackAfterAddingNodes(mapData.arrayEntry, mapData.mappedNodes, mapData.indexObservable);
                mapData.initialized = true;
                lastNode = mapData.mappedNodes[mapData.mappedNodes.length - 1];     // get the last node again since it may have been changed by a preprocessor
            }
        }

        // Restore the focused element if it had lost focus
        if (activeElement && domNode.ownerDocument.activeElement != activeElement) {
            activeElement.focus();
        }

        // If there's a beforeRemove callback, call it after reordering.
        // Note that we assume that the beforeRemove callback will usually be used to remove the nodes using
        // some sort of animation, which is why we first reorder the nodes that will be removed. If the
        // callback instead removes the nodes right away, it would be more efficient to skip reordering them.
        // Perhaps we'll make that change in the future if this scenario becomes more common.
        callCallback(options['beforeRemove'], itemsForBeforeRemoveCallbacks);

        // Replace the stored values of deleted items with a dummy value. This provides two benefits: it marks this item
        // as already "removed" so we won't call beforeRemove for it again, and it ensures that the item won't match up
        // with an actual item in the array and appear as "retained" or "moved".
        for (i = 0; i < itemsForBeforeRemoveCallbacks.length; ++i) {
            itemsForBeforeRemoveCallbacks[i].arrayEntry = deletedItemDummyValue;
        }

        // Finally call afterMove and afterAdd callbacks
        callCallback(options['afterMove'], itemsForMoveCallbacks);
        callCallback(options['afterAdd'], itemsForAfterAddCallbacks);
    }
})();

ko.exportSymbol('utils.setDomNodeChildrenFromArrayMapping', ko.utils.setDomNodeChildrenFromArrayMapping);
ko.nativeTemplateEngine = function () {
    this['allowTemplateRewriting'] = false;
}

ko.nativeTemplateEngine.prototype = new ko.templateEngine();
ko.nativeTemplateEngine.prototype.constructor = ko.nativeTemplateEngine;
ko.nativeTemplateEngine.prototype['renderTemplateSource'] = function (templateSource, bindingContext, options, templateDocument) {
    var useNodesIfAvailable = !(ko.utils.ieVersion < 9), // IE<9 cloneNode doesn't work properly
        templateNodesFunc = useNodesIfAvailable ? templateSource['nodes'] : null,
        templateNodes = templateNodesFunc ? templateSource['nodes']() : null;

    if (templateNodes) {
        return ko.utils.makeArray(templateNodes.cloneNode(true).childNodes);
    } else {
        var templateText = templateSource['text']();
        return ko.utils.parseHtmlFragment(templateText, templateDocument);
    }
};

ko.nativeTemplateEngine.instance = new ko.nativeTemplateEngine();
ko.setTemplateEngine(ko.nativeTemplateEngine.instance);

ko.exportSymbol('nativeTemplateEngine', ko.nativeTemplateEngine);
(function() {
    ko.jqueryTmplTemplateEngine = function () {
        // Detect which version of jquery-tmpl you're using. Unfortunately jquery-tmpl
        // doesn't expose a version number, so we have to infer it.
        // Note that as of Knockout 1.3, we only support jQuery.tmpl 1.0.0pre and later,
        // which KO internally refers to as version "2", so older versions are no longer detected.
        var jQueryTmplVersion = this.jQueryTmplVersion = (function() {
            if (!jQueryInstance || !(jQueryInstance['tmpl']))
                return 0;
            // Since it exposes no official version number, we use our own numbering system. To be updated as jquery-tmpl evolves.
            try {
                if (jQueryInstance['tmpl']['tag']['tmpl']['open'].toString().indexOf('__') >= 0) {
                    // Since 1.0.0pre, custom tags should append markup to an array called "__"
                    return 2; // Final version of jquery.tmpl
                }
            } catch(ex) { /* Apparently not the version we were looking for */ }

            return 1; // Any older version that we don't support
        })();

        function ensureHasReferencedJQueryTemplates() {
            if (jQueryTmplVersion < 2)
                throw new Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");
        }

        function executeTemplate(compiledTemplate, data, jQueryTemplateOptions) {
            return jQueryInstance['tmpl'](compiledTemplate, data, jQueryTemplateOptions);
        }

        this['renderTemplateSource'] = function(templateSource, bindingContext, options, templateDocument) {
            templateDocument = templateDocument || document;
            options = options || {};
            ensureHasReferencedJQueryTemplates();

            // Ensure we have stored a precompiled version of this template (don't want to reparse on every render)
            var precompiled = templateSource['data']('precompiled');
            if (!precompiled) {
                var templateText = templateSource['text']() || "";
                // Wrap in "with($whatever.koBindingContext) { ... }"
                templateText = "{{ko_with $item.koBindingContext}}" + templateText + "{{/ko_with}}";

                precompiled = jQueryInstance['template'](null, templateText);
                templateSource['data']('precompiled', precompiled);
            }

            var data = [bindingContext['$data']]; // Prewrap the data in an array to stop jquery.tmpl from trying to unwrap any arrays
            var jQueryTemplateOptions = jQueryInstance['extend']({ 'koBindingContext': bindingContext }, options['templateOptions']);

            var resultNodes = executeTemplate(precompiled, data, jQueryTemplateOptions);
            resultNodes['appendTo'](templateDocument.createElement("div")); // Using "appendTo" forces jQuery/jQuery.tmpl to perform necessary cleanup work

            jQueryInstance['fragments'] = {}; // Clear jQuery's fragment cache to avoid a memory leak after a large number of template renders
            return resultNodes;
        };

        this['createJavaScriptEvaluatorBlock'] = function(script) {
            return "{{ko_code ((function() { return " + script + " })()) }}";
        };

        this['addTemplate'] = function(templateName, templateMarkup) {
            document.write("<script type='text/html' id='" + templateName + "'>" + templateMarkup + "<" + "/script>");
        };

        if (jQueryTmplVersion > 0) {
            jQueryInstance['tmpl']['tag']['ko_code'] = {
                open: "__.push($1 || '');"
            };
            jQueryInstance['tmpl']['tag']['ko_with'] = {
                open: "with($1) {",
                close: "} "
            };
        }
    };

    ko.jqueryTmplTemplateEngine.prototype = new ko.templateEngine();
    ko.jqueryTmplTemplateEngine.prototype.constructor = ko.jqueryTmplTemplateEngine;

    // Use this one by default *only if jquery.tmpl is referenced*
    var jqueryTmplTemplateEngineInstance = new ko.jqueryTmplTemplateEngine();
    if (jqueryTmplTemplateEngineInstance.jQueryTmplVersion > 0)
        ko.setTemplateEngine(jqueryTmplTemplateEngineInstance);

    ko.exportSymbol('jqueryTmplTemplateEngine', ko.jqueryTmplTemplateEngine);
})();
}));
}());
})();
;
!function(t){"use strict";if("function"==typeof bootstrap)bootstrap("promise",t);else if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define(t);else if("undefined"!=typeof ses){if(!ses.ok())return;ses.makeQ=t}else{if("undefined"==typeof window&&"undefined"==typeof self)throw new Error("This environment was not anticipated by Q. Please file a bug.");var n="undefined"!=typeof window?window:self,e=n.Q;n.Q=t(),n.Q.noConflict=function(){return n.Q=e,this}}}(function(){"use strict";function t(t){return function(){return K.apply(t,arguments)}}function n(t){return t===Object(t)}function e(t){return"[object StopIteration]"===en(t)||t instanceof _}function r(t,n){if(V&&n.stack&&"object"==typeof t&&null!==t&&t.stack&&-1===t.stack.indexOf(rn)){for(var e=[],r=n;r;r=r.source)r.stack&&e.unshift(r.stack);e.unshift(t.stack);var i=e.join("\n"+rn+"\n");t.stack=o(i)}}function o(t){for(var n=t.split("\n"),e=[],r=0;r<n.length;++r){var o=n[r];c(o)||i(o)||!o||e.push(o)}return e.join("\n")}function i(t){return-1!==t.indexOf("(module.js:")||-1!==t.indexOf("(node.js:")}function u(t){var n=/at .+ \((.+):(\d+):(?:\d+)\)$/.exec(t);if(n)return[n[1],Number(n[2])];var e=/at ([^ ]+):(\d+):(?:\d+)$/.exec(t);if(e)return[e[1],Number(e[2])];var r=/.*@(.+):(\d+)$/.exec(t);return r?[r[1],Number(r[2])]:void 0}function c(t){var n=u(t);if(!n)return!1;var e=n[0],r=n[1];return e===H&&r>=q&&fn>=r}function s(){if(V)try{throw new Error}catch(t){var n=t.stack.split("\n"),e=n[0].indexOf("@")>0?n[1]:n[2],r=u(e);if(!r)return;return H=r[0],r[1]}}function f(t,n,e){return function(){return"undefined"!=typeof console&&"function"==typeof console.warn&&console.warn(n+" is deprecated, use "+e+" instead.",new Error("").stack),t.apply(t,arguments)}}function p(t){return t instanceof h?t:g(t)?O(t):E(t)}function a(){function t(t){n=t,i.source=t,W(e,function(n,e){p.nextTick(function(){t.promiseDispatch.apply(t,e)})},void 0),e=void 0,r=void 0}var n,e=[],r=[],o=Z(a.prototype),i=Z(h.prototype);if(i.promiseDispatch=function(t,o,i){var u=L(arguments);e?(e.push(u),"when"===o&&i[1]&&r.push(i[1])):p.nextTick(function(){n.promiseDispatch.apply(n,u)})},i.valueOf=function(){if(e)return i;var t=v(n);return m(t)&&(n=t),t},i.inspect=function(){return n?n.inspect():{state:"pending"}},p.longStackSupport&&V)try{throw new Error}catch(u){i.stack=u.stack.substring(u.stack.indexOf("\n")+1)}return o.promise=i,o.resolve=function(e){n||t(p(e))},o.fulfill=function(e){n||t(E(e))},o.reject=function(e){n||t(R(e))},o.notify=function(t){n||W(r,function(n,e){p.nextTick(function(){e(t)})},void 0)},o}function l(t){if("function"!=typeof t)throw new TypeError("resolver must be a function.");var n=a();try{t(n.resolve,n.reject,n.notify)}catch(e){n.reject(e)}return n.promise}function d(t){return l(function(n,e){for(var r=0,o=t.length;o>r;r++)p(t[r]).then(n,e)})}function h(t,n,e){void 0===n&&(n=function(t){return R(new Error("Promise does not support operation: "+t))}),void 0===e&&(e=function(){return{state:"unknown"}});var r=Z(h.prototype);if(r.promiseDispatch=function(e,o,i){var u;try{u=t[o]?t[o].apply(r,i):n.call(r,o,i)}catch(c){u=R(c)}e&&e(u)},r.inspect=e,e){var o=e();"rejected"===o.state&&(r.exception=o.reason),r.valueOf=function(){var t=e();return"pending"===t.state||"rejected"===t.state?r:t.value}}return r}function y(t,n,e,r){return p(t).then(n,e,r)}function v(t){if(m(t)){var n=t.inspect();if("fulfilled"===n.state)return n.value}return t}function m(t){return t instanceof h}function g(t){return n(t)&&"function"==typeof t.then}function k(t){return m(t)&&"pending"===t.inspect().state}function j(t){return!m(t)||"fulfilled"===t.inspect().state}function w(t){return m(t)&&"rejected"===t.inspect().state}function b(){on.length=0,un.length=0,sn||(sn=!0)}function x(t,n){sn&&("object"==typeof process&&"function"==typeof process.emit&&p.nextTick.runAfter(function(){-1!==X(un,t)&&(process.emit("unhandledRejection",n,t),cn.push(t))}),un.push(t),on.push(n&&"undefined"!=typeof n.stack?n.stack:"(no stack) "+n))}function T(t){if(sn){var n=X(un,t);-1!==n&&("object"==typeof process&&"function"==typeof process.emit&&p.nextTick.runAfter(function(){var e=X(cn,t);-1!==e&&(process.emit("rejectionHandled",on[n],t),cn.splice(e,1))}),un.splice(n,1),on.splice(n,1))}}function R(t){var n=h({when:function(n){return n&&T(this),n?n(t):this}},function(){return this},function(){return{state:"rejected",reason:t}});return x(n,t),n}function E(t){return h({when:function(){return t},get:function(n){return t[n]},set:function(n,e){t[n]=e},"delete":function(n){delete t[n]},post:function(n,e){return null===n||void 0===n?t.apply(void 0,e):t[n].apply(t,e)},apply:function(n,e){return t.apply(n,e)},keys:function(){return nn(t)}},void 0,function(){return{state:"fulfilled",value:t}})}function O(t){var n=a();return p.nextTick(function(){try{t.then(n.resolve,n.reject,n.notify)}catch(e){n.reject(e)}}),n.promise}function S(t){return h({isDef:function(){}},function(n,e){return A(t,n,e)},function(){return p(t).inspect()})}function N(t,n,e){return p(t).spread(n,e)}function D(t){return function(){function n(t,n){var u;if("undefined"==typeof StopIteration){try{u=r[t](n)}catch(c){return R(c)}return u.done?p(u.value):y(u.value,o,i)}try{u=r[t](n)}catch(c){return e(c)?p(c.value):R(c)}return y(u,o,i)}var r=t.apply(this,arguments),o=n.bind(n,"next"),i=n.bind(n,"throw");return o()}}function P(t){p.done(p.async(t)())}function C(t){throw new _(t)}function Q(t){return function(){return N([this,I(arguments)],function(n,e){return t.apply(n,e)})}}function A(t,n,e){return p(t).dispatch(n,e)}function I(t){return y(t,function(t){var n=0,e=a();return W(t,function(r,o,i){var u;m(o)&&"fulfilled"===(u=o.inspect()).state?t[i]=u.value:(++n,y(o,function(r){t[i]=r,0===--n&&e.resolve(t)},e.reject,function(t){e.notify({index:i,value:t})}))},void 0),0===n&&e.resolve(t),e.promise})}function U(t){if(0===t.length)return p.resolve();var n=p.defer(),e=0;return W(t,function(r,o,i){function u(t){n.resolve(t)}function c(){e--,0===e&&n.reject(new Error("Can't get fulfillment value from any promise, all promises were rejected."))}function s(t){n.notify({index:i,value:t})}var f=t[i];e++,y(f,u,c,s)},void 0),n.promise}function F(t){return y(t,function(t){return t=Y(t,p),y(I(Y(t,function(t){return y(t,z,z)})),function(){return t})})}function M(t){return p(t).allSettled()}function B(t,n){return p(t).then(void 0,void 0,n)}function $(t,n){return p(t).nodeify(n)}var V=!1;try{throw new Error}catch(G){V=!!G.stack}var H,_,q=s(),z=function(){},J=function(){function t(){for(var t,r;e.next;)e=e.next,t=e.task,e.task=void 0,r=e.domain,r&&(e.domain=void 0,r.enter()),n(t,r);for(;c.length;)t=c.pop(),n(t);o=!1}function n(n,e){try{n()}catch(r){if(u)throw e&&e.exit(),setTimeout(t,0),e&&e.enter(),r;setTimeout(function(){throw r},0)}e&&e.exit()}var e={task:void 0,next:null},r=e,o=!1,i=void 0,u=!1,c=[];if(J=function(t){r=r.next={task:t,domain:u&&process.domain,next:null},o||(o=!0,i())},"object"==typeof process&&"[object process]"===process.toString()&&process.nextTick)u=!0,i=function(){process.nextTick(t)};else if("function"==typeof setImmediate)i="undefined"!=typeof window?setImmediate.bind(window,t):function(){setImmediate(t)};else if("undefined"!=typeof MessageChannel){var s=new MessageChannel;s.port1.onmessage=function(){i=f,s.port1.onmessage=t,t()};var f=function(){s.port2.postMessage(0)};i=function(){setTimeout(t,0),f()}}else i=function(){setTimeout(t,0)};return J.runAfter=function(t){c.push(t),o||(o=!0,i())},J}(),K=Function.call,L=t(Array.prototype.slice),W=t(Array.prototype.reduce||function(t,n){var e=0,r=this.length;if(1===arguments.length)for(;;){if(e in this){n=this[e++];break}if(++e>=r)throw new TypeError}for(;r>e;e++)e in this&&(n=t(n,this[e],e));return n}),X=t(Array.prototype.indexOf||function(t){for(var n=0;n<this.length;n++)if(this[n]===t)return n;return-1}),Y=t(Array.prototype.map||function(t,n){var e=this,r=[];return W(e,function(o,i,u){r.push(t.call(n,i,u,e))},void 0),r}),Z=Object.create||function(t){function n(){}return n.prototype=t,new n},tn=t(Object.prototype.hasOwnProperty),nn=Object.keys||function(t){var n=[];for(var e in t)tn(t,e)&&n.push(e);return n},en=t(Object.prototype.toString);_="undefined"!=typeof ReturnValue?ReturnValue:function(t){this.value=t};var rn="From previous event:";p.resolve=p,p.nextTick=J,p.longStackSupport=!1,"object"==typeof process&&process&&process.env&&process.env.Q_DEBUG&&(p.longStackSupport=!0),p.defer=a,a.prototype.makeNodeResolver=function(){var t=this;return function(n,e){n?t.reject(n):t.resolve(arguments.length>2?L(arguments,1):e)}},p.Promise=l,p.promise=l,l.race=d,l.all=I,l.reject=R,l.resolve=p,p.passByCopy=function(t){return t},h.prototype.passByCopy=function(){return this},p.join=function(t,n){return p(t).join(n)},h.prototype.join=function(t){return p([this,t]).spread(function(t,n){if(t===n)return t;throw new Error("Can't join: not the same: "+t+" "+n)})},p.race=d,h.prototype.race=function(){return this.then(p.race)},p.makePromise=h,h.prototype.toString=function(){return"[object Promise]"},h.prototype.then=function(t,n,e){function o(n){try{return"function"==typeof t?t(n):n}catch(e){return R(e)}}function i(t){if("function"==typeof n){r(t,c);try{return n(t)}catch(e){return R(e)}}return R(t)}function u(t){return"function"==typeof e?e(t):t}var c=this,s=a(),f=!1;return p.nextTick(function(){c.promiseDispatch(function(t){f||(f=!0,s.resolve(o(t)))},"when",[function(t){f||(f=!0,s.resolve(i(t)))}])}),c.promiseDispatch(void 0,"when",[void 0,function(t){var n,e=!1;try{n=u(t)}catch(r){if(e=!0,!p.onerror)throw r;p.onerror(r)}e||s.notify(n)}]),s.promise},p.tap=function(t,n){return p(t).tap(n)},h.prototype.tap=function(t){return t=p(t),this.then(function(n){return t.fcall(n).thenResolve(n)})},p.when=y,h.prototype.thenResolve=function(t){return this.then(function(){return t})},p.thenResolve=function(t,n){return p(t).thenResolve(n)},h.prototype.thenReject=function(t){return this.then(function(){throw t})},p.thenReject=function(t,n){return p(t).thenReject(n)},p.nearer=v,p.isPromise=m,p.isPromiseAlike=g,p.isPending=k,h.prototype.isPending=function(){return"pending"===this.inspect().state},p.isFulfilled=j,h.prototype.isFulfilled=function(){return"fulfilled"===this.inspect().state},p.isRejected=w,h.prototype.isRejected=function(){return"rejected"===this.inspect().state};var on=[],un=[],cn=[],sn=!0;p.resetUnhandledRejections=b,p.getUnhandledReasons=function(){return on.slice()},p.stopUnhandledRejectionTracking=function(){b(),sn=!1},b(),p.reject=R,p.fulfill=E,p.master=S,p.spread=N,h.prototype.spread=function(t,n){return this.all().then(function(n){return t.apply(void 0,n)},n)},p.async=D,p.spawn=P,p["return"]=C,p.promised=Q,p.dispatch=A,h.prototype.dispatch=function(t,n){var e=this,r=a();return p.nextTick(function(){e.promiseDispatch(r.resolve,t,n)}),r.promise},p.get=function(t,n){return p(t).dispatch("get",[n])},h.prototype.get=function(t){return this.dispatch("get",[t])},p.set=function(t,n,e){return p(t).dispatch("set",[n,e])},h.prototype.set=function(t,n){return this.dispatch("set",[t,n])},p.del=p["delete"]=function(t,n){return p(t).dispatch("delete",[n])},h.prototype.del=h.prototype["delete"]=function(t){return this.dispatch("delete",[t])},p.mapply=p.post=function(t,n,e){return p(t).dispatch("post",[n,e])},h.prototype.mapply=h.prototype.post=function(t,n){return this.dispatch("post",[t,n])},p.send=p.mcall=p.invoke=function(t,n){return p(t).dispatch("post",[n,L(arguments,2)])},h.prototype.send=h.prototype.mcall=h.prototype.invoke=function(t){return this.dispatch("post",[t,L(arguments,1)])},p.fapply=function(t,n){return p(t).dispatch("apply",[void 0,n])},h.prototype.fapply=function(t){return this.dispatch("apply",[void 0,t])},p["try"]=p.fcall=function(t){return p(t).dispatch("apply",[void 0,L(arguments,1)])},h.prototype.fcall=function(){return this.dispatch("apply",[void 0,L(arguments)])},p.fbind=function(t){var n=p(t),e=L(arguments,1);return function(){return n.dispatch("apply",[this,e.concat(L(arguments))])}},h.prototype.fbind=function(){var t=this,n=L(arguments);return function(){return t.dispatch("apply",[this,n.concat(L(arguments))])}},p.keys=function(t){return p(t).dispatch("keys",[])},h.prototype.keys=function(){return this.dispatch("keys",[])},p.all=I,h.prototype.all=function(){return I(this)},p.any=U,h.prototype.any=function(){return U(this)},p.allResolved=f(F,"allResolved","allSettled"),h.prototype.allResolved=function(){return F(this)},p.allSettled=M,h.prototype.allSettled=function(){return this.then(function(t){return I(Y(t,function(t){function n(){return t.inspect()}return t=p(t),t.then(n,n)}))})},p.fail=p["catch"]=function(t,n){return p(t).then(void 0,n)},h.prototype.fail=h.prototype["catch"]=function(t){return this.then(void 0,t)},p.progress=B,h.prototype.progress=function(t){return this.then(void 0,void 0,t)},p.fin=p["finally"]=function(t,n){return p(t)["finally"](n)},h.prototype.fin=h.prototype["finally"]=function(t){return t=p(t),this.then(function(n){return t.fcall().then(function(){return n})},function(n){return t.fcall().then(function(){throw n})})},p.done=function(t,n,e,r){return p(t).done(n,e,r)},h.prototype.done=function(t,n,e){var o=function(t){p.nextTick(function(){if(r(t,i),!p.onerror)throw t;p.onerror(t)})},i=t||n||e?this.then(t,n,e):this;"object"==typeof process&&process&&process.domain&&(o=process.domain.bind(o)),i.then(void 0,o)},p.timeout=function(t,n,e){return p(t).timeout(n,e)},h.prototype.timeout=function(t,n){var e=a(),r=setTimeout(function(){n&&"string"!=typeof n||(n=new Error(n||"Timed out after "+t+" ms"),n.code="ETIMEDOUT"),e.reject(n)},t);return this.then(function(t){clearTimeout(r),e.resolve(t)},function(t){clearTimeout(r),e.reject(t)},e.notify),e.promise},p.delay=function(t,n){return void 0===n&&(n=t,t=void 0),p(t).delay(n)},h.prototype.delay=function(t){return this.then(function(n){var e=a();return setTimeout(function(){e.resolve(n)},t),e.promise})},p.nfapply=function(t,n){return p(t).nfapply(n)},h.prototype.nfapply=function(t){var n=a(),e=L(t);return e.push(n.makeNodeResolver()),this.fapply(e).fail(n.reject),n.promise},p.nfcall=function(t){var n=L(arguments,1);return p(t).nfapply(n)},h.prototype.nfcall=function(){var t=L(arguments),n=a();return t.push(n.makeNodeResolver()),this.fapply(t).fail(n.reject),n.promise},p.nfbind=p.denodeify=function(t){var n=L(arguments,1);return function(){var e=n.concat(L(arguments)),r=a();return e.push(r.makeNodeResolver()),p(t).fapply(e).fail(r.reject),r.promise}},h.prototype.nfbind=h.prototype.denodeify=function(){var t=L(arguments);return t.unshift(this),p.denodeify.apply(void 0,t)},p.nbind=function(t,n){var e=L(arguments,2);return function(){function r(){return t.apply(n,arguments)}var o=e.concat(L(arguments)),i=a();return o.push(i.makeNodeResolver()),p(r).fapply(o).fail(i.reject),i.promise}},h.prototype.nbind=function(){var t=L(arguments,0);return t.unshift(this),p.nbind.apply(void 0,t)},p.nmapply=p.npost=function(t,n,e){return p(t).npost(n,e)},h.prototype.nmapply=h.prototype.npost=function(t,n){var e=L(n||[]),r=a();return e.push(r.makeNodeResolver()),this.dispatch("post",[t,e]).fail(r.reject),r.promise},p.nsend=p.nmcall=p.ninvoke=function(t,n){var e=L(arguments,2),r=a();return e.push(r.makeNodeResolver()),p(t).dispatch("post",[n,e]).fail(r.reject),r.promise},h.prototype.nsend=h.prototype.nmcall=h.prototype.ninvoke=function(t){var n=L(arguments,1),e=a();return n.push(e.makeNodeResolver()),this.dispatch("post",[t,n]).fail(e.reject),e.promise},p.nodeify=$,h.prototype.nodeify=function(t){return t?void this.then(function(n){p.nextTick(function(){t(null,n)})},function(n){p.nextTick(function(){t(n)})}):this},p.noConflict=function(){throw new Error("Q.noConflict only works when Q is used as a global")};var fn=s();return p});;
/*
 * Copyright 2012-2016 IdeaBlade, Inc.  All Rights Reserved.  
 * Use, reproduction, distribution, and modification of this code is subject to the terms and 
 * conditions of the IdeaBlade Breeze license, available at http://www.breezejs.com/license
 *
 * Author: Jay Traband
 */

(function (global, definition) {
    var def = function(){ return definition(global); };

    // CommonJS
    if (typeof exports === "object" && typeof module === "object") {
        module.exports = def();
        // RequireJS
    } else if (typeof define === "function" && define["amd"]) {
        define(def);
        // <script>
    } else {
        breeze = def();
    }

})(this, function (global) {
    "use strict"; 
    var breeze = {
        version: "1.6.3",
        metadataVersion: "1.0.5"
    };
    ;/**
 @module core
 **/

var __hasOwnProperty = uncurry(Object.prototype.hasOwnProperty);
var __arraySlice = uncurry(Array.prototype.slice);
var __isES5Supported = function () {
  try {
    return !!(Object.getPrototypeOf && Object.defineProperty({}, 'x', {}));
  } catch (e) {
    return false;
  }
}();

// iterate over object
function __objectForEach(obj, kvFn) {
  for (var key in obj) {
    if (__hasOwnProperty(obj, key)) {
      kvFn(key, obj[key]);
    }
  }
}

function __objectMap(obj, kvFn) {
  var results = [];
  for (var key in obj) {
    if (__hasOwnProperty(obj, key)) {
      var result = kvFn ? kvFn(key, obj[key]) : obj[key];
      if (result !== undefined) {
        results.push(result);
      }
    }
  }
  return results;
}

function __objectFirst(obj, kvPredicate) {
  for (var key in obj) {
    if (__hasOwnProperty(obj, key)) {
      var value = obj[key];
      if (kvPredicate(key, value)) {
        return { key: key, value: value };
      }
    }
  }
  return null;
}

function __isSettable(entity, propertyName) {
  var pd = __getPropDescriptor(entity, propertyName);
  if (pd == null) return true;
  return !!(pd.writable || pd.set);
}

function __getPropDescriptor(obj, propertyName) {
  if (!__isES5Supported) return null;

  if (obj.hasOwnProperty(propertyName)) {
    return Object.getOwnPropertyDescriptor(obj, propertyName);
  } else {
    var nextObj = Object.getPrototypeOf(obj);
    if (nextObj == null) return null;
    return __getPropDescriptor(nextObj, propertyName);
  }
}

// Functional extensions

// can be used like: persons.filter(propEq("firstName", "John"))
function __propEq(propertyName, value) {
  return function (obj) {
    return obj[propertyName] === value;
  };
}

// can be used like persons.map(pluck("firstName"))
function __pluck(propertyName) {
  return function (obj) {
    return obj[propertyName];
  };
}

// end functional extensions


function __getOwnPropertyValues(source) {
  var result = [];
  for (var name in source) {
    if (__hasOwnProperty(source, name)) {
      result.push(source[name]);
    }
  }
  return result;
}

function __extend(target, source, propNames) {
  if (!source) return target;
  if (propNames) {
    propNames.forEach(function (propName) {
      target[propName] = source[propName];
    });
  } else {
    for (var propName in source) {
      if (__hasOwnProperty(source, propName)) {
        target[propName] = source[propName];
      }
    }
  }
  return target;
}


function __updateWithDefaults(target, defaults) {
  for (var name in defaults) {
    if (target[name] === undefined) {
      target[name] = defaults[name];
    }
  }
  return target;
}


function __setAsDefault(target, ctor) {
  // we want to insure that the object returned by ctor.defaultInstance is always immutable
  // Use 'target' as the primary template for the ctor.defaultInstance;
  // Use current 'ctor.defaultInstance' as the template for any missing properties
  // creates a new instance for ctor.defaultInstance
  // returns target unchanged
  ctor.defaultInstance = __updateWithDefaults(new ctor(target), ctor.defaultInstance);
  return target;
}

// 'source' is an object that will be transformed into another
// 'template' is a map where the
//    keys: are the keys to return
//      if a key contains ','s then the key is treated as a delimited string with first of the
//      keys being the key to return and the others all valid aliases for this key
//    'values' are either
//        1) the 'default' value of the key
//        2) a function that takes in the source value and should return the value to set
//      The value from the source is then set on the target,
//      after first passing thru the fn, if provided, UNLESS:
//        1) it is the default value
//        2) it is undefined ( nulls WILL be set)
// 'target' is optional
//    - if it exists then properties of the target will be set ( overwritten if the exist)
//    - if it does not exist then a new object will be created as filled.
// 'target is returned.
function __toJson(source, template, target) {
  target = target || {};

  for (var key in template) {
    var aliases = key.split(",");
    var defaultValue = template[key];
    // using some as a forEach with a 'break'
    aliases.some(function(propName) {
      if (!(propName in source)) return false;
      var value = source[propName];
      // there is a functional property defined with this alias ( not what we want to replace).
      if (typeof value == 'function') return false;
      // '==' is deliberate here - idea is that null or undefined values will never get serialized
      // if default value is set to null.
      if (value == defaultValue) return true;
      if (Array.isArray(value) && value.length === 0) return true;
      if (typeof(defaultValue) === "function") {
        value = defaultValue(value);
      } else if (typeof (value) === "object") {
        if (value && value.parentEnum) {
          value = value.name;
        }
      }
      if (value === undefined) return true;
      target[aliases[0]] = value;
      return true;
    });
  }
  return target;
}

// default replacer function for __toJSONSafe.  Excludes entityAspect and other internal properties
function __safeReplacer(prop, val) {
	if (prop === "entityAspect" || prop === "complexAspect" || prop === "entityType" || prop === "complexType"
	|| prop === "constructor" || prop.charAt(0) === '_' || prop.charAt(0) === '$') return;
	return val;
}

// safely perform toJSON logic on objects with cycles.
function __toJSONSafe(obj, replacer) {
  if (obj !== Object(obj)) return obj; // primitive value
  if (obj._$visited) return undefined;
  replacer = replacer || __safeReplacer;
  if (obj.toJSON) {
    var newObj = obj.toJSON();
    if (newObj !== Object(newObj)) return newObj; // primitive value
    if (newObj !== obj) return __toJSONSafe(newObj, replacer);
    // toJSON returned the object unchanged.
    obj = newObj;
  }
  obj._$visited = true;
  var result;
  if (obj instanceof Array) {
    result = obj.map(function (o) {
      return __toJSONSafe(o, replacer);
    });
  } else if (typeof (obj) === "function") {
    result = undefined;
  } else {
    result = {};
    for (var prop in obj) {
      if (prop === "_$visited") continue;
      var val = obj[prop];
      if (replacer) {
        val = replacer(prop, val);
        if (val === undefined) continue;
      }
      val = __toJSONSafe(val, replacer);
      if (val === undefined) continue;
      result[prop] = val;
    }
  }
  delete obj._$visited;
  return result;
}

// resolves the values of a list of properties by checking each property in multiple sources until a value is found.
function __resolveProperties(sources, propertyNames) {
  var r = {};
  var length = sources.length;
  propertyNames.forEach(function (pn) {
    for (var i = 0; i < length; i++) {
      var src = sources[i];
      if (src) {
        var val = src[pn];
        if (val !== undefined) {
          r[pn] = val;
          break;
        }
      }
    }
  });
  return r;
}


// array functions

function __toArray(item) {
  if (item == null) {
    return [];
  } else if (Array.isArray(item)) {
    return item;
  } else {
    return [item];
  }
}

// a version of Array.map that doesn't require an array, i.e. works on arrays and scalars.
function __map(items, fn, includeNull) {
  // whether to return nulls in array of results; default = true;
  includeNull = includeNull == null ? true : includeNull;
  if (items == null) return items;
  var result;
  if (Array.isArray(items)) {
    result = [];
    items.forEach(function (v, ix) {
      var r = fn(v, ix);
      if (r != null || includeNull) {
        result[ix] = r;
      }
    });
  } else {
    result = fn(items);
  }
  return result;
}


function __arrayFirst(array, predicate) {
  for (var i = 0, j = array.length; i < j; i++) {
    if (predicate(array[i])) {
      return array[i];
    }
  }
  return null;
}

function __arrayIndexOf(array, predicate) {
  for (var i = 0, j = array.length; i < j; i++) {
    if (predicate(array[i])) return i;
  }
  return -1;
}

function __arrayAddItemUnique(array, item) {
  var ix = array.indexOf(item);
  if (ix === -1) array.push(item);
}

function __arrayRemoveItem(array, predicateOrItem, shouldRemoveMultiple) {
  var predicate = __isFunction(predicateOrItem) ? predicateOrItem : undefined;
  var lastIx = array.length - 1;
  var removed = false;
  for (var i = lastIx; i >= 0; i--) {
    if (predicate ? predicate(array[i]) : (array[i] === predicateOrItem)) {
      array.splice(i, 1);
      removed = true;
      if (!shouldRemoveMultiple) {
        return true;
      }
    }
  }
  return removed;
}

function __arrayZip(a1, a2, callback) {
  var result = [];
  var n = Math.min(a1.length, a2.length);
  for (var i = 0; i < n; ++i) {
    result.push(callback(a1[i], a2[i]));
  }
  return result;
}

//function __arrayDistinct(array) {
//    array = array || [];
//    var result = [];
//    for (var i = 0, j = array.length; i < j; i++) {
//        if (result.indexOf(array[i]) < 0)
//            result.push(array[i]);
//    }
//    return result;
//}

// Not yet needed
//// much faster but only works on array items with a toString method that
//// returns distinct string for distinct objects.  So this is safe for arrays with primitive
//// types but not for arrays with object types, unless toString() has been implemented.
//function arrayDistinctUnsafe(array) {
//    var o = {}, i, l = array.length, r = [];
//    for (i = 0; i < l; i += 1) {
//        var v = array[i];
//        o[v] = v;
//    }
//    for (i in o) r.push(o[i]);
//    return r;
//}

function __arrayEquals(a1, a2, equalsFn) {
  //Check if the arrays are undefined/null
  if (!a1 || !a2) return false;

  if (a1.length !== a2.length) return false;

  //go thru all the vars
  for (var i = 0; i < a1.length; i++) {
    //if the var is an array, we need to make a recursive check
    //otherwise we'll just compare the values
    if (Array.isArray(a1[i])) {
      if (!__arrayEquals(a1[i], a2[i])) return false;
    } else {
      if (equalsFn) {
        if (!equalsFn(a1[i], a2[i])) return false;
      } else {
        if (a1[i] !== a2[i]) return false;
      }
    }
  }
  return true;
}

// end of array functions

// returns and array for a source and a prop, and creates the prop if needed.
function __getArray(source, propName) {
  var arr = source[propName];
  if (!arr) {
    arr = [];
    source[propName] = arr;
  }
  return arr;
}

function __requireLib(libNames, errMessage) {
  var arrNames = libNames.split(";");
  for (var i = 0, j = arrNames.length; i < j; i++) {
    var lib = __requireLibCore(arrNames[i]);
    if (lib) return lib;
  }
  if (errMessage) {
    throw new Error("Unable to initialize " + libNames + ".  " + errMessage);
  }
}

// Returns the 'libName' module if loaded or else returns undefined
function __requireLibCore(libName) {
  var window = global.window;
  if (!window) return; // Must run in a browser. Todo: add commonjs support

  // get library from browser globals if we can
  var lib = window[libName];
  if (lib) return lib;

  // if require exists, maybe require can get it.
  // This method is synchronous so it can't load modules with AMD.
  // It can only obtain modules from require that have already been loaded.
  // Developer should bootstrap such that the breeze module
  // loads after all other libraries that breeze should find with this method
  // See documentation
  var r = window.require;
  if (r) { // if require exists
    if (r.defined) { // require.defined is not standard and may not exist
      // require.defined returns true if module has been loaded
      return r.defined(libName) ? r(libName) : undefined;
    } else {
      // require.defined does not exist so we have to call require('libName') directly.
      // The require('libName') overload is synchronous and does not load modules.
      // It throws an exception if the module isn't already loaded.
      try {
        return r(libName);
      } catch (e) {
        // require('libName') threw because module not loaded
        return;
      }
    }
  }
}

function __using(obj, property, tempValue, fn) {
  var originalValue = obj[property];
  if (tempValue === originalValue) {
    return fn();
  }
  obj[property] = tempValue;
  try {
    return fn();
  } finally {
    if (originalValue === undefined) {
      delete obj[property];
    } else {
      obj[property] = originalValue;
    }
  }
}

function __wrapExecution(startFn, endFn, fn) {
  var state;
  try {
    state = startFn();
    return fn();
  } catch (e) {
    if (typeof(state) === 'object') {
      state.error = e;
    }
    throw e;
  } finally {
    endFn(state);
  }
}

function __memoize(fn) {
  return function () {
    var args = __arraySlice(arguments),
        hash = "",
        i = args.length,
        currentArg = null;
    while (i--) {
      currentArg = args[i];
      hash += (currentArg === Object(currentArg)) ? JSON.stringify(currentArg) : currentArg;
      fn.memoize || (fn.memoize = {});
    }
    return (hash in fn.memoize) ?
           fn.memoize[hash] :
           fn.memoize[hash] = fn.apply(this, args);
  };
}

function __getUuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    //noinspection NonShortCircuitBooleanExpressionJS
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

function __durationToSeconds(duration) {
  // basic algorithm from https://github.com/nezasa/iso8601-js-period
  if (typeof duration !== "string") throw new Error("Invalid ISO8601 duration '" + duration + "'");

  // regex splits as follows - grp0, grp1, y, m, d, grp2, h, m, s
  //                           0     1     2  3  4  5     6  7  8
  var struct = /^P((\d+Y)?(\d+M)?(\d+D)?)?(T(\d+H)?(\d+M)?(\d+S)?)?$/.exec(duration);
  if (!struct) throw new Error("Invalid ISO8601 duration '" + duration + "'");

  var ymdhmsIndexes = [2, 3, 4, 6, 7, 8]; // -> grp1,y,m,d,grp2,h,m,s
  var factors = [31104000, // year (360*24*60*60)
                 2592000,             // month (30*24*60*60)
                 86400,               // day (24*60*60)
                 3600,                // hour (60*60)
                 60,                  // minute (60)
                 1];                  // second (1)

  var seconds = 0;
  for (var i = 0; i < 6; i++) {
    var digit = struct[ymdhmsIndexes[i]];
    // remove letters, replace by 0 if not defined
    digit = digit ? +digit.replace(/[A-Za-z]+/g, '') : 0;
    seconds += digit * factors[i];
  }
  return seconds;

}

// is functions

function __noop() {
  // does nothing
}

function __identity(x) {
  return x;
}

function __classof(o) {
  if (o === null) {
    return "null";
  }
  if (o === undefined) {
    return "undefined";
  }
  return Object.prototype.toString.call(o).slice(8, -1).toLowerCase();
}

function __isDate(o) {
  return __classof(o) === "date" && !isNaN(o.getTime());
}

function __isDateString(s) {
  // var rx = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
  var rx = /^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/;
  return (typeof s === "string") && rx.test(s);
}

function __isFunction(o) {
  return __classof(o) === "function";
}

function __isString(o) {
  return (typeof o === "string");
}

function __isObject(o) {
  return (typeof o === "object");
}

function __isGuid(value) {
  return (typeof value === "string") && /[a-fA-F\d]{8}-(?:[a-fA-F\d]{4}-){3}[a-fA-F\d]{12}/.test(value);
}

function __isDuration(value) {
  return (typeof value === "string") && /^(-|)?P[T]?[\d\.,\-]+[YMDTHS]/.test(value);
}

function __isEmpty(obj) {
  if (obj === null || obj === undefined) {
    return true;
  }
  for (var key in obj) {
    if (__hasOwnProperty(obj, key)) {
      return false;
    }
  }
  return true;
}

function __isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

// returns true for booleans, numbers, strings and dates
// false for null, and non-date objects, functions, and arrays
function __isPrimitive(obj) {
  if (obj == null) return false;
  // true for numbers, strings, booleans and null, false for objects
  if (obj != Object(obj)) return true;
  return __isDate(obj);
}

// end of is Functions

// string functions

function __stringStartsWith(str, prefix) {
  // returns true for empty string or null prefix
  if ((!str)) return false;
  if (prefix == "" || prefix == null) return true;
  return str.indexOf(prefix, 0) === 0;
}

function __stringEndsWith(str, suffix) {
  // returns true for empty string or null suffix
  if ((!str)) return false;
  if (suffix == "" || suffix == null) return true;
  return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

// Based on fragment from Dean Edwards' Base 2 library
// format("a %1 and a %2", "cat", "dog") -> "a cat and a dog"
function __formatString(string) {
  var args = arguments;
  var pattern = RegExp("%([1-" + (arguments.length - 1) + "])", "g");
  return string.replace(pattern, function (match, index) {
    return args[index];
  });
}

// Change text to title case with spaces, e.g. 'myPropertyName12' to 'My Property Name 12'
// See http://stackoverflow.com/questions/7225407/convert-camelcasetext-to-camel-case-text
var __camelEdges = /([A-Z](?=[A-Z][a-z])|[^A-Z](?=[A-Z])|[a-zA-Z](?=[^a-zA-Z]))/g;
function __titleCaseSpace(text) {
  text = text.replace(__camelEdges, '$1 ');
  text = text.charAt(0).toUpperCase() + text.slice(1);
  return text;
}

// end of string functions

// See Mark Miller’s explanation of what this does.
// http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming
function uncurry(f) {
  var call = Function.call;
  return function () {
    return call.apply(f, arguments);
  };
}

// shims

if (!Object.create) {
  Object.create = function (parent) {
    var F = function () {
    };
    F.prototype = parent;
    return new F();
  };
}

var core = {};

// not all methods above are exported
core.__isES5Supported = __isES5Supported;

core.objectForEach = __objectForEach;

core.extend = __extend;
core.propEq = __propEq;
core.pluck = __pluck;

core.arrayEquals = __arrayEquals;
core.arrayFirst = __arrayFirst;
core.arrayIndexOf = __arrayIndexOf;
core.arrayRemoveItem = __arrayRemoveItem;
core.arrayZip = __arrayZip;

core.requireLib = __requireLib;
core.using = __using;

core.memoize = __memoize;
core.getUuid = __getUuid;
core.durationToSeconds = __durationToSeconds;

core.isDate = __isDate;
core.isGuid = __isGuid;
core.isDuration = __isDuration;
core.isFunction = __isFunction;
core.isEmpty = __isEmpty;
core.isNumeric = __isNumeric;

core.stringStartsWith = __stringStartsWith;
core.stringEndsWith = __stringEndsWith;
core.formatString = __formatString;
core.titleCase = __titleCaseSpace;

core.getPropertyDescriptor = __getPropDescriptor;

core.toJSONSafe = __toJSONSafe;
core.toJSONSafeReplacer = __safeReplacer;

core.parent = breeze;
breeze.core = core;


;/**
 @module core
 **/

var Param = (function () {
  // The %1 parameter
  // is required
  // must be a %2
  // must be an instance of %2
  // must be an instance of the %2 enumeration
  // must have a %2 property
  // must be an array where each element
  // is optional or

  var ctor = function (v, name) {
    this.v = v;
    this.name = name;
    this._contexts = [null];

  };
  var proto = ctor.prototype;

  proto.isObject = function () {
    return this.isTypeOf("object");
  };

  proto.isBoolean = function () {
    return this.isTypeOf('boolean');
  };

  proto.isString = function () {
    return this.isTypeOf('string');
  };

  proto.isNonEmptyString = function () {
    return addContext(this, {
      fn: isNonEmptyString,
      msg: "must be a nonEmpty string"
    });
  };

  function isNonEmptyString(context, v) {
    if (v == null) return false;
    return (typeof(v) === 'string') && v.length > 0;
  }

  proto.isNumber = function () {
    return this.isTypeOf('number');
  };

  proto.isFunction = function () {
    return this.isTypeOf('function');
  };


  proto.isTypeOf = function (typeName) {
    return addContext(this, {
      fn: isTypeOf,
      typeName: typeName,
       msg: "must be a '" + typeName + "'"
    });
  };

  function isTypeOf(context, v) {
    if (v == null) return false;
    if (typeof(v) === context.typeName) return true;
    return false;
  }

  proto.isInstanceOf = function (type, typeName) {
    typeName = typeName || type.prototype._$typeName;
    return addContext(this, {
      fn: isInstanceOf,
      type: type,
      typeName: typeName,
      msg: "must be an instance of '" + typeName + "'"
    });
  };

  function isInstanceOf(context, v) {
    if (v == null) return false;
    return (v instanceof context.type);
  }

  proto.hasProperty = function (propertyName) {
    return addContext(this, {
      fn: hasProperty,
      propertyName: propertyName,
      msg: "must have a '" + propertyName + "' property"
    });
  };

  function hasProperty(context, v) {
    if (v == null) return false;
    return (v[context.propertyName] !== undefined);
  }

  proto.isEnumOf = function (enumType) {
    return addContext(this, {
      fn: isEnumOf,
      enumType: enumType,
      msg: "must be an instance of the '" + enumType.name + "' enumeration"
    });
  };

  function isEnumOf(context, v) {
    if (v == null) return false;
    return context.enumType.contains(v);
  }

  proto.isRequired = function (allowNull) {
    return addContext(this, {
      fn: isRequired,
      allowNull: allowNull,
      msg: "is required"
    });
  };

  function isRequired(context, v) {
    if (context.allowNull) {
      return v !== undefined;
    } else {
      return v != null;
    }
  }

  // combinable methods.

  proto.isOptional = function () {
    var context = {
      fn: isOptional,
      prevContext: null,
      msg: isOptionalMessage
    };
    return addContext(this, context);
  };

  function isOptional(context, v) {
    if (v == null) return true;
    var prevContext = context.prevContext;
    if (prevContext) {
      return prevContext.fn(prevContext, v);
    } else {
      return true;
    }
  }

  function isOptionalMessage(context, v) {
    var prevContext = context.prevContext;
    var element = prevContext ? " or it " + getMessage(prevContext, v) : "";
    return "is optional" + element;
  }

  proto.isNonEmptyArray = function () {
    return this.isArray(true);
  };

  proto.isArray = function (mustNotBeEmpty) {
    var context = {
      fn: isArray,
      mustNotBeEmpty: mustNotBeEmpty,
      prevContext: null,
      msg: isArrayMessage
    };
    return addContext(this, context);
  };


  function isArray(context, v) {
    if (!Array.isArray(v)) {
      return false;
    }
    if (context.mustNotBeEmpty) {
      if (v.length === 0) return false;
    }
    // allow standalone is array call.
    var prevContext = context.prevContext;
    if (!prevContext) return true;

    return v.every(function (v1) {
      return prevContext.fn(prevContext, v1);
    });
  }

  function isArrayMessage(context, v) {
    var arrayDescr = context.mustNotBeEmpty ? "a nonEmpty array" : "an array";
    var prevContext = context.prevContext;
    var element = prevContext ? " where each element " + getMessage(prevContext, v) : "";
    return " must be " + arrayDescr + element;
  }

  function getMessage(context, v) {
    var msg = context.msg;
    if (typeof(msg) === "function") {
      msg = msg(context, v);
    }
    return msg;
  }

  proto.or = function () {
    this._contexts.push(null);
    this._context = null;
    return this;
  };

  proto.check = function (defaultValue) {
    var ok = exec(this);
    if (ok === undefined) return;
    if (!ok) {
      throw new Error(this.getMessage());
    }

    if (this.v !== undefined) {
      return this.v;
    } else {
      return defaultValue;
    }
  };

  // called from outside this file.
  proto._addContext = function (context) {
    return addContext(this, context);
  };

  function addContext(that, context) {
    if (that._context) {
      var curContext = that._context;

      while (curContext.prevContext != null) {
        curContext = curContext.prevContext;
      }

      if (curContext.prevContext === null) {
        curContext.prevContext = context;
        // just update the prevContext but don't change the curContext.
        return that;
      } else if (context.prevContext == null) {
        context.prevContext = that._context;
      } else {
        throw new Error("Illegal construction - use 'or' to combine checks");
      }
    }
    return setContext(that, context);
  }

  function setContext(that, context) {
    that._contexts[that._contexts.length - 1] = context;
    that._context = context;
    return that;
  }


  function exec(self) {
    // clear off last one if null
    var contexts = self._contexts;
    if (contexts[contexts.length - 1] == null) {
      contexts.pop();
    }
    if (contexts.length === 0) {
      return undefined;
    }
    return contexts.some(function (context) {
      return context.fn(context, self.v);
    });
  }


  proto.getMessage = function () {
    var that = this;
    var message = this._contexts.map(function (context) {
      return getMessage(context, that.v);
    }).join(", or it ");
    return __formatString(this.MESSAGE_PREFIX, this.name) + " " + message;
  };

  proto.withDefault = function (defaultValue) {
    this.defaultValue = defaultValue;
    return this;
  };

  proto.whereParam = function (propName) {
    return this.parent.whereParam(propName);
  };


  proto.applyAll = function (instance, checkOnly) {
    var parentTypeName = instance._$typeName;
    var allowUnknownProperty = (parentTypeName && this.parent.config._$typeName === parentTypeName);

    var clone = __extend({}, this.parent.config);
    this.parent.params.forEach(function (p) {
      if (!allowUnknownProperty) delete clone[p.name];
      try {
        p.check();
      } catch (e) {
        throwConfigError(instance, e.message);
      }
      (!checkOnly) && p._applyOne(instance);
    });
    // should be no properties left in the clone
    if (!allowUnknownProperty) {
      for (var key in clone) {
        // allow props with an undefined value
        if (clone[key] !== undefined) {
          throwConfigError(instance, __formatString("Unknown property: '%1'.", key));
        }
      }
    }
  };

  function throwConfigError(instance, message) {
    throw new Error(__formatString("Error configuring an instance of '%1'. %2", (instance && instance._$typeName) || "object", message));
  }

  proto._applyOne = function (instance) {
    if (this.v !== undefined) {
      instance[this.name] = this.v;
    } else {
      if (this.defaultValue !== undefined) {
        instance[this.name] = this.defaultValue;
      }
    }
  };

  proto.MESSAGE_PREFIX = "The '%1' parameter ";
  return ctor;
})();

var assertParam = function (v, name) {
  return new Param(v, name);
};

var ConfigParam = (function () {
  var ctor = function (config) {
    if (typeof(config) !== "object") {
      throw new Error("Configuration parameter should be an object, instead it is a: " + typeof(config));
    }
    this.config = config;
    this.params = [];
  };
  var proto = ctor.prototype;

  proto.whereParam = function (propName) {
    var param = new Param(this.config[propName], propName);
    param.parent = this;
    this.params.push(param);
    return param;
  };
  return ctor;
})();

var assertConfig = function (config) {
  return new ConfigParam(config);
};

// Param is exposed so that additional 'is' methods can be added to the prototype.
core.Param = Param;
core.assertParam = assertParam;
core.assertConfig = assertConfig;



;/**
@module core
**/

var Enum = (function () {

  // TODO: think about CompositeEnum (flags impl).

  /**
  Base class for all Breeze enumerations, such as EntityState, DataType, FetchStrategy, MergeStrategy etc.
  A Breeze Enum is a namespaced set of constant values.  Each Enum consists of a group of related constants, called 'symbols'.
  Unlike enums in some other environments, each 'symbol' can have both methods and properties.

  @example
      // Example of creating a new Enum
      var prototype = {
          nextDay: function () {
              var nextIndex = (this.dayIndex+1) % 7;
              return DayOfWeek.getSymbols()[nextIndex];
          }
      };

      var DayOfWeek = new Enum("DayOfWeek", prototype);
      DayOfWeek.Monday    = DayOfWeek.addSymbol( { dayIndex: 0 });
      DayOfWeek.Tuesday   = DayOfWeek.addSymbol( { dayIndex: 1 });
      DayOfWeek.Wednesday = DayOfWeek.addSymbol( { dayIndex: 2 });
      DayOfWeek.Thursday  = DayOfWeek.addSymbol( { dayIndex: 3 });
      DayOfWeek.Friday    = DayOfWeek.addSymbol( { dayIndex: 4 });
      DayOfWeek.Saturday  = DayOfWeek.addSymbol( { dayIndex: 5, isWeekend: true });
      DayOfWeek.Sunday    = DayOfWeek.addSymbol( { dayIndex: 6, isWeekend: true });
      DayOfWeek.resolveSymbols();

      // custom methods
      ok(DayOfWeek.Monday.nextDay() === DayOfWeek.Tuesday);
      ok(DayOfWeek.Sunday.nextDay() === DayOfWeek.Monday);
      // custom properties
      ok(DayOfWeek.Tuesday.isWeekend === undefined);
      ok(DayOfWeek.Saturday.isWeekend == true);
      // Standard enum capabilities
      ok(DayOfWeek instanceof Enum);
      ok(Enum.isSymbol(DayOfWeek.Wednesday));
      ok(DayOfWeek.contains(DayOfWeek.Thursday));
      ok(DayOfWeek.Tuesday.parentEnum == DayOfWeek);
      ok(DayOfWeek.getSymbols().length === 7);
      ok(DayOfWeek.Friday.toString() === "Friday");


  @class Enum
  **/

  /**
  Enum constructor - may be used to create new Enums.
  @example
      var prototype = {
          nextDay: function () {
              var nextIndex = (this.dayIndex+1) % 7;
              return DayOfWeek.getSymbols()[nextIndex];
          }
      };

      var DayOfWeek = new Enum("DayOfWeek", prototype);
  @method <ctor> Enum
  @param name {String}
  @param [methodObj] {Object}
  **/

  var ctor = function Enum(name, methodObj) {
    this.name = name;
    var prototype = new EnumSymbol(methodObj);
    prototype.parentEnum = this;
    this._symbolPrototype = prototype;
    if (methodObj) {
      Object.keys(methodObj).forEach(function (key) {
        prototype[key] = methodObj[key];
      });
    }
  };
  var proto = ctor.prototype;

  /**
  Checks if an object is an Enum 'symbol'.
  @example
      if (Enum.isSymbol(DayOfWeek.Wednesday)) {
        // do something ...
      };
  @method isSymbol
  @return {Boolean}
  @static
  **/
  ctor.isSymbol = function (obj) {
    return obj instanceof EnumSymbol;
  };

  /**
  Returns an Enum symbol given its name.
  @example
      var dayOfWeek = DayOfWeek.from("Thursday");
      // nowdayOfWeek === DayOfWeek.Thursday
  @method fromName
  @param name {String} Name for which an enum symbol should be returned.
  @return {EnumSymbol} The symbol that matches the name or 'undefined' if not found.
  **/
  proto.fromName = function (name) {
    return this[name];
  };

  /**
  Adds a new symbol to an Enum.
  @example
      var DayOfWeek = new Enum("DayOfWeek", prototype);
      DayOfWeek.Monday    = DayOfWeek.addSymbol( { dayIndex: 0 });
  @method addSymbol
  @param [propertiesObj] {Object} A collection of properties that should be added to the new symbol.
  In other words, the 'propertiesObj' is any state that should be held by the symbol.
  @return {EnumSymbol} The new symbol
  **/
  proto.addSymbol = function (propertiesObj) {
    // TODO: check if sealed.
    var newSymbol = Object.create(this._symbolPrototype);
    if (propertiesObj) {
      Object.keys(propertiesObj).forEach(function (key) {
        newSymbol[key] = propertiesObj[key];
      });
    }
    setTimeout(function () {
      newSymbol.getName();
    }, 0);
    return newSymbol;
  };

  /**
  Seals this enum so that no more symbols may be added to it. This should only be called after all symbols
  have already been added to the Enum.
  @example
      DayOfWeek.resolveSymbols();
  @method resolveSymbols
  **/
  proto.resolveSymbols = function () {
    this.getSymbols().forEach(function (sym) {
      return sym.getName();
    });
  };

  //// TODO: remove or rethink this.
  //Enum.prototype.combineSymbols = function () {
  //    var proto = this._symbolPrototype;
  //    var newSymbol = Object.create(proto);
  //    newSymbol._symbols = __arraySlice(arguments);

  //    Object.keys(proto).forEach(function (key) {
  //        var result;
  //        var oldMethod = proto[key];
  //        if (__isFunction(oldMethod)) {
  //            var newMethod = function () {

  //                if (this._symbols) {
  //                    result = this._symbols.map(function (sym) {
  //                        return oldMethod.apply(sym);
  //                    });
  //                } else {
  //                    result = oldMethod.apply(this);
  //                }
  //                return result;
  //            };
  //            proto[key] = newMethod;
  //        }
  //    });
  //    return newSymbol;
  //};

  /**
  Returns all of the symbols contained within this Enum.
  @example
      var symbols = DayOfWeek.getSymbols();
  @method getSymbols
  @return {Array of EnumSymbol} All of the symbols contained within this Enum.
  **/
  proto.getSymbols = function () {
    return this.getNames().map(function (key) {
      return this[key];
    }, this);
  };

  /**
  Returns the names of all of the symbols contained within this Enum.
  @example
      var symbols = DayOfWeek.getNames();
  @method getNames
  @return {Array of String} All of the names of the symbols contained within this Enum.
  **/
  proto.getNames = function () {
    var result = [];
    for (var key in this) {
      if (this.hasOwnProperty(key)) {
        if (key !== "name" && key.substr(0, 1) !== "_" && !__isFunction(this[key])) {
          result.push(key);
        }
      }
    }
    return result;
  };

  /**
  Returns whether an Enum contains a specified symbol.
  @example
      var symbol = DayOfWeek.Friday;
      if (DayOfWeek.contains(symbol)) {
          // do something
      }
  @method contains
  @param {Object} Object or symbol to test.
  @return {Boolean} Whether this Enum contains the specified symbol.
  **/
  proto.contains = function (sym) {
    if (!(sym instanceof EnumSymbol)) {
      return false;
    }
    return this[sym.getName()] === sym;
  };

  /**
  One of the constant values that is generated by the {{#crossLink "Enum"}}{{/crossLink}} "addSymbol" method.  EnumSymbols should ONLY be created via
  the Enum.addSymbol method.
  @example
      var DayOfWeek = new Enum("DayOfWeek");
      DayOfWeek.Monday    = DayOfWeek.addSymbol();
  @class EnumSymbol
  **/

  function EnumSymbol() {
  }

  /**
  The {{#crossLink "Enum"}}{{/crossLink}} to which this symbol belongs.
  __readOnly__
  @property parentEnum {Enum}
  **/

  /**
  Returns the name of this symbol.
  @example
      var name = DayOfWeek.Monday.getName();
      // name === "Monday"
  @method getName
  **/
  EnumSymbol.prototype.getName = function () {
    if (!this.name) {
      var that = this;
      this.name = __arrayFirst(this.parentEnum.getNames(), function (name) {
        return that.parentEnum[name] === that;
      });
    }
    return this.name;
  };

  /**
  Same as the getName method. Returns the name of this symbol.
  @example
      var name = DayOfWeek.Monday.toString();
      // name === "Monday"
  @method toString
  **/
  EnumSymbol.prototype.toString = function () {
    return this.getName();
  };

  EnumSymbol.prototype.toJSON = function () {
    return {
      _$typeName: this.parentEnum.name,
      name: this.name
    };
  };

  return ctor;
})();
core.Enum = Enum;


;/**
@module core
**/

var Event = (function () {

  var __eventNameMap = {};
  var __nextUnsubKey = 1;

  /**
  Class to support basic event publication and subscription semantics.
  @class Event
  **/

  /**
  Constructor for an Event
  @example
      salaryEvent = new Event("salaryEvent", person);
  @method <ctor> Event
  @param name {String}
  @param publisher {Object} The object that will be doing the publication. i.e. the object to which this event is attached.
  @param [defaultErrorCallback] {Function} If omitted then subscriber notification failures will be ignored.

  errorCallback([e])
  @param [defaultErrorCallback.e] {Error} Any error encountered during subscription execution.
  **/

  var ctor = function Event(name, publisher, defaultErrorCallback) {
    assertParam(name, "eventName").isNonEmptyString().check();
    assertParam(publisher, "publisher").isObject().check();

    this.name = name;
    // register the name
    __eventNameMap[name] = true;
    this.publisher = publisher;
    if (defaultErrorCallback) {
      this._defaultErrorCallback = defaultErrorCallback;
    }
  };
  var proto = ctor.prototype;

  /**
  Publish data for this event.
  @example
      // Assume 'salaryEvent' is previously constructed Event
      salaryEvent.publish( { eventType: "payRaise", amount: 100 });
  This event can also be published asychronously
  @example
      salaryEvent.publish( { eventType: "payRaise", amount: 100 }, true);
  And we can add a handler in case the subscriber 'mishandles' the event.
  @example
      salaryEvent.publish( { eventType: "payRaise", amount: 100 }, true, function(error) {
          // do something with the 'error' object
      });
  @method publish
  @param data {Object} Data to publish
  @param [publishAsync=false] {Boolean} Whether to publish asynchonously or not.
  @param [errorCallback] {Function} Will be called for any errors that occur during publication. If omitted,
  errors will be eaten.

  errorCallback([e])
  @param [errorCallback.e] {Error} Any error encountered during publication execution.
  @return {Boolean} false if event is disabled; true otherwise.
  **/
  proto.publish = function (data, publishAsync, errorCallback) {

    if (!ctor._isEnabled(this.name, this.publisher)) return false;

    if (publishAsync === true) {
      setTimeout(publishCore, 0, this, data, errorCallback);
    } else {
      publishCore(this, data, errorCallback);
    }
    return true;
  };

  function publishCore(that, data, errorCallback) {
    var subscribers = that._subscribers;
    if (!subscribers) return true;
    // subscribers from outer scope.
    subscribers.forEach(function (s) {
      try {
        s.callback(data);
      } catch (e) {
        e.context = "unable to publish on topic: " + that.name;
        if (errorCallback) {
          errorCallback(e);
        } else if (that._defaultErrorCallback) {
          that._defaultErrorCallback(e);
        } else {
          fallbackErrorHandler(e);
        }
      }
    });
  }

  /**
  Publish data for this event asynchronously.
  @example
      // Assume 'salaryEvent' is previously constructed Event
      salaryEvent.publishAsync( { eventType: "payRaise", amount: 100 });
  And we can add a handler in case the subscriber 'mishandles' the event.
  @example
      salaryEvent.publishAsync( { eventType: "payRaise", amount: 100 }, function(error) {
          // do something with the 'error' object
      });
  @method publishAsync
  @param data {Object} Data to publish
  @param [errorCallback] {Function} Will be called for any errors that occur during publication. If omitted,
  errors will be eaten.

  errorCallback([e])
  @param [errorCallback.e] {Error} Any error encountered during publication execution.
  **/
  proto.publishAsync = function (data, errorCallback) {
    this.publish(data, true, errorCallback);
  };

  /**
  Subscribe to this event.
  @example
      // Assume 'salaryEvent' is previously constructed Event
      salaryEvent.subscribe(function (eventArgs) {
          if (eventArgs.eventType === "payRaise") {
              // do something
          }
      });
  There are several built in Breeze events, such as EntityAspect.propertyChanged, EntityAspect.validationErrorsChanged as well.
  @example
      // Assume order is a preexisting 'order' entity
      order.entityAspect.propertyChanged.subscribe(function (pcEvent) {
          if ( pcEvent.propertyName === "OrderDate") {
              // do something
          }
      });
  @method subscribe
  @param [callback] {Function} Will be called whenever 'data' is published for this event.

  callback([data])
  @param [callback.data] {Object} Whatever 'data' was published.  This should be documented on the specific event.
  @return {Number} This is a key for 'unsubscription'.  It can be passed to the 'unsubscribe' method.
  **/
  proto.subscribe = function (callback) {
    if (!this._subscribers) {
      this._subscribers = [];
    }

    var unsubKey = __nextUnsubKey;
    this._subscribers.push({ unsubKey: unsubKey, callback: callback });
    ++__nextUnsubKey;
    return unsubKey;
  };

  /**
  Unsubscribe from this event.
  @example
      // Assume order is a preexisting 'order' entity
      var token = order.entityAspect.propertyChanged.subscribe(function (pcEvent) {
              // do something
      });
      // sometime later
      order.entityAspect.propertyChanged.unsubscribe(token);
  @method unsubscribe
  @param unsubKey {Number} The value returned from the 'subscribe' method may be used to unsubscribe here.
  @return {Boolean} Whether unsubscription occured. This will return false if already unsubscribed or if the key simply
  cannot be found.
  **/
  proto.unsubscribe = function (unsubKey) {
    if (!this._subscribers) return false;
    var subs = this._subscribers;
    var ix = __arrayIndexOf(subs, function (s) {
      return s.unsubKey === unsubKey;
    });
    if (ix !== -1) {
      subs.splice(ix, 1);
      if (subs.length === 0) {
        this._subscribers = null;
      }
      return true;
    } else {
      return false;
    }
  };

  proto.clear = function () {
    this._subscribers = null;
  };

  // event bubbling - document later.
  ctor.bubbleEvent = function (target, getParentFn) {
    target._getEventParent = getParentFn;
  };

  /**
  Enables or disables the named event for an object and all of its children.
  @example
      Event.enable(“propertyChanged”, myEntityManager, false)
  will disable all EntityAspect.propertyChanged events within a EntityManager.
  @example
      Event.enable(“propertyChanged”, myEntityManager, true)
  will enable all EntityAspect.propertyChanged events within a EntityManager.
  @example
      Event.enable(“propertyChanged”, myEntity.entityAspect, false)
  will disable EntityAspect.propertyChanged events for a specific entity.
  @example
      Event.enable(“propertyChanged”, myEntity.entityAspect, null)
  will removes any enabling / disabling at the entity aspect level so now any 'Event.enable' calls at the EntityManager level,
  made either previously or in the future, will control notification.
  @example
      Event.enable(“validationErrorsChanged”, myEntityManager, function(em) {
          return em.customTag === “blue”;
      })
  will either enable or disable myEntityManager based on the current value of a ‘customTag’ property on myEntityManager.
  Note that this is dynamic, changing the customTag value will cause events to be enabled or disabled immediately.
  @method enable
  @static
  @param eventName {String} The name of the event.
  @param target {Object} The object at which enabling or disabling will occur.  All event notifications that occur to this object or
  children of this object will be enabled or disabled.
  @param isEnabled {Boolean|null|Function} A boolean, a null or a function that returns either a boolean or a null.
  **/
  ctor.enable = function (eventName, obj, isEnabled) {
    assertParam(eventName, "eventName").isNonEmptyString().check();
    assertParam(obj, "obj").isObject().check();
    assertParam(isEnabled, "isEnabled").isBoolean().isOptional().or().isFunction().check();
    if (!obj._$eventMap) {
      obj._$eventMap = {};
    }
    obj._$eventMap[eventName] = isEnabled;
  };


  /**
  Returns whether for a specific event and a specific object and its children, notification is enabled or disabled or not set.
  @example
      Event.isEnabled(“propertyChanged”, myEntityManager)

  @method isEnabled
  @static
  @param eventName {String} The name of the event.
  @param target {Object} The object for which we want to know if notifications are enabled.
  @return {Boolean|null} A null is returned if this value has not been set.
  **/
  ctor.isEnabled = function (eventName, obj) {
    assertParam(eventName, "eventName").isNonEmptyString().check();
    assertParam(obj, "obj").isObject().check();
    // null is ok - it just means that the object is at the top level.
    if (obj._getEventParent === undefined) {
      throw new Error("This object does not support event enabling/disabling");
    }
    // return ctor._isEnabled(getFullEventName(eventName), obj);
    return ctor._isEnabled(eventName, obj);
  };

  ctor._isEnabled = function (eventName, obj) {
    var isEnabled = null;
    var eventMap = obj._$eventMap;
    if (eventMap) {
      isEnabled = eventMap[eventName];
    }
    if (isEnabled != null) {
      if (typeof isEnabled === 'function') {
        return isEnabled(obj);
      } else {
        return !!isEnabled;
      }
    } else {
      var parent = obj._getEventParent && obj._getEventParent();
      if (parent) {
        return ctor._isEnabled(eventName, parent);
      } else {
        // default if not explicitly disabled.
        return true;
      }
    }
  };

  function fallbackErrorHandler(e) {
    // TODO: maybe log this
    // for now do nothing;
  }

  return ctor;

})();

core.Event = Event;;/**
@module breeze
**/

var __config = (function () {

  // alias for within fns with a config param
  var __config = {};

  __config.functionRegistry = {};
  __config.typeRegistry = {};
  __config.objectRegistry = {};
  __config.interfaceInitialized = new Event("interfaceInitialized", __config);

  var InterfaceDef = function (name) {
    this.name = name;
    this.defaultInstance = null;
    this._implMap = {};
  };

  InterfaceDef.prototype.registerCtor = function (adapterName, ctor) {
    this._implMap[adapterName.toLowerCase()] = { ctor: ctor, defaultInstance: null };
  };
  InterfaceDef.prototype.getImpl = function (adapterName) {
    return this._implMap[adapterName.toLowerCase()];
  };
  InterfaceDef.prototype.getFirstImpl = function () {
    var kv = __objectFirst(this._implMap, function () {
      return true;
    });
    return kv ? kv.value : null;
  };

  __config.interfaceRegistry = {
    ajax: new InterfaceDef("ajax"),
    modelLibrary: new InterfaceDef("modelLibrary"),
    dataService: new InterfaceDef("dataService"),
    uriBuilder: new InterfaceDef("uriBuilder")
  };

  __config.interfaceRegistry.modelLibrary.getDefaultInstance = function () {
    if (!this.defaultInstance) {
      throw new Error("Unable to locate the default implementation of the '" + this.name +
          "' interface.  Possible options are 'ko', 'backingStore' or 'backbone'. See the breeze.config.initializeAdapterInstances method.");
    }
    return this.defaultInstance;
  };

  /**
  A singleton object that is the repository of all configuration options.
  @example
      config.initializeAdapterInstance( {
          modelLibrary: "ko",
          dataService: "webApi"
      });

  @class config
  **/

  /**
  This method is now OBSOLETE.  Use the "initializeAdapterInstances" to accomplish the same result.
  @method setProperties
  @deprecated
  @param config {Object}
  @param [config.remoteAccessImplementation] { implementation of remoteAccess-interface }
  @param [config.trackingImplementation] { implementation of entityTracking-interface }
  @param [config.ajaxImplementation] {implementation of ajax-interface }
  **/
  __config.setProperties = function (config) {
    assertConfig(config)
        .whereParam("remoteAccessImplementation").isOptional()
        .whereParam("trackingImplementation").isOptional()
        .whereParam("ajaxImplementation").isOptional()
        .applyAll(config);
    if (config.remoteAccessImplementation) {
      __config.initializeAdapterInstance("dataService", config.remoteAccessImplementation);
    }
    if (config.trackingImplementation) {
      // note the name change
      __config.initializeAdapterInstance("modelLibrary", config.trackingImplementation);
    }
    if (config.ajaxImplementation) {
      __config.initializeAdapterInstance("ajax", config.ajaxImplementation);
    }
  };

  /**
  Method use to register implementations of standard breeze interfaces.  Calls to this method are usually
  made as the last step within an adapter implementation.
  @method registerAdapter
  @param interfaceName {String} - one of the following interface names "ajax", "dataService" or "modelLibrary"
  @param adapterCtor {Function} - an ctor function that returns an instance of the specified interface.
  **/
  __config.registerAdapter = function (interfaceName, adapterCtor) {
    assertParam(interfaceName, "interfaceName").isNonEmptyString().check();
    assertParam(adapterCtor, "adapterCtor").isFunction().check();
    // this impl will be thrown away after the name is retrieved.
    var impl = new adapterCtor();
    var implName = impl.name;
    if (!implName) {
      throw new Error("Unable to locate a 'name' property on the constructor passed into the 'registerAdapter' call.");
    }
    var idef = getInterfaceDef(interfaceName);
    idef.registerCtor(implName, adapterCtor);

  };

  /**
  Returns the ctor function used to implement a specific interface with a specific adapter name.
  @method getAdapter
  @param interfaceName {String} One of the following interface names "ajax", "dataService" or "modelLibrary"
  @param [adapterName] {String} The name of any previously registered adapter. If this parameter is omitted then
  this method returns the "default" adapter for this interface. If there is no default adapter, then a null is returned.
  @return {Function|null} Returns either a ctor function or null.
  **/
  __config.getAdapter = function (interfaceName, adapterName) {
    var idef = getInterfaceDef(interfaceName);
    if (adapterName) {
      var impl = idef.getImpl(adapterName);
      return impl ? impl.ctor : null;
    } else {
      return idef.defaultInstance ? idef.defaultInstance._$impl.ctor : null;
    }
  };

  /**
  Initializes a collection of adapter implementations and makes each one the default for its corresponding interface.
  @method initializeAdapterInstances
  @param config {Object}
  @param [config.ajax] {String} - the name of a previously registered "ajax" adapter
  @param [config.dataService] {String} - the name of a previously registered "dataService" adapter
  @param [config.modelLibrary] {String} - the name of a previously registered "modelLibrary" adapter
  @param [config.uriBuilder] {String} - the name of a previously registered "uriBuilder" adapter
  @return [array of instances]
  **/
  __config.initializeAdapterInstances = function (config) {
    assertConfig(config)
        .whereParam("dataService").isOptional()
        .whereParam("modelLibrary").isOptional()
        .whereParam("ajax").isOptional()
        .whereParam("uriBuilder").isOptional()
        .applyAll(this, false);
    return __objectMap(config, __config.initializeAdapterInstance);

  };

  /**
  Initializes a single adapter implementation. Initialization means either newing a instance of the
  specified interface and then calling "initialize" on it or simply calling "initialize" on the instance
  if it already exists.
  @method initializeAdapterInstance
  @param interfaceName {String} The name of the interface to which the adapter to initialize belongs.
  @param adapterName {String} - The name of a previously registered adapter to initialize.
  @param [isDefault=true] {Boolean} - Whether to make this the default "adapter" for this interface.
  @return {an instance of the specified adapter}
  **/
  __config.initializeAdapterInstance = function (interfaceName, adapterName, isDefault) {
    isDefault = isDefault === undefined ? true : isDefault;
    assertParam(interfaceName, "interfaceName").isNonEmptyString().check();
    assertParam(adapterName, "adapterName").isNonEmptyString().check();
    assertParam(isDefault, "isDefault").isBoolean().check();

    var idef = getInterfaceDef(interfaceName);
    var impl = idef.getImpl(adapterName);
    if (!impl) {
      throw new Error("Unregistered adapter.  Interface: " + interfaceName + " AdapterName: " + adapterName);
    }

    return initializeAdapterInstanceCore(idef, impl, isDefault);
  };

  /**
  Returns the adapter instance corresponding to the specified interface and adapter names.
  @method getAdapterInstance
  @param interfaceName {String} The name of the interface.
  @param [adapterName] {String} - The name of a previously registered adapter.  If this parameter is
  omitted then the default implementation of the specified interface is returned. If there is
  no defaultInstance of this interface, then the first registered instance of this interface is returned.
  @return {an instance of the specified adapter}
  **/
  __config.getAdapterInstance = function (interfaceName, adapterName) {
    var idef = getInterfaceDef(interfaceName);
    var impl;

    var isDefault = adapterName == null || adapterName == "";
    if (isDefault) {
      if (idef.defaultInstance) return idef.defaultInstance;
      impl = idef.getFirstImpl();
    } else {
      impl = idef.getImpl(adapterName);
    }
    if (!impl) return null;
    if (impl.defaultInstance) {
      return impl.defaultInstance;
    } else {
      return initializeAdapterInstanceCore(idef, impl, isDefault);
    }
  };

  // this is needed for reflection purposes when deserializing an object that needs a fn or ctor
  // used to register validators.
  __config.registerFunction = function (fn, fnName) {
    assertParam(fn, "fn").isFunction().check();
    assertParam(fnName, "fnName").isString().check();
    fn.prototype._$fnName = fnName;
    __config.functionRegistry[fnName] = fn;
  };

  __config.getRegisteredFunction = function (fnName) {
    return __config.functionRegistry[fnName];
  };

  __config._storeObject = function (obj, type, name) {
    // uncomment this if we make this public.
    //assertParam(obj, "obj").isObject().check();
    //assertParam(name, "objName").isString().check();
    var key = (typeof(type) === "string" ? type : type.prototype._$typeName) + "." + name;
    __config.objectRegistry[key] = obj;
  };

  __config._fetchObject = function (type, name) {
    if (!name) return undefined;
    var key = (typeof(type) === "string" ? type : type.prototype._$typeName) + "." + name;
    var result = __config.objectRegistry[key];
    if (!result) {
      throw new Error("Unable to locate a registered object by the name: " + key);
    }
    return result;
  };

  __config.registerType = function (ctor, typeName) {
    assertParam(ctor, "ctor").isFunction().check();
    assertParam(typeName, "typeName").isString().check();
    ctor.prototype._$typeName = typeName;
    __config.typeRegistry[typeName] = ctor;
  };

  __config.stringifyPad = '';

  function initializeAdapterInstanceCore(interfaceDef, impl, isDefault) {
    var instance = impl.defaultInstance;
    if (!instance) {
      instance = new (impl.ctor)();
      impl.defaultInstance = instance;
      instance._$impl = impl;
    }

    instance.initialize();

    if (isDefault) {
      // next line needs to occur before any recomposition
      interfaceDef.defaultInstance = instance;
    }

    // recomposition of other impls will occur here.
    __config.interfaceInitialized.publish({ interfaceName: interfaceDef.name, instance: instance, isDefault: true });

    if (instance.checkForRecomposition) {
      // now register for own dependencies.
      __config.interfaceInitialized.subscribe(function (interfaceInitializedArgs) {
        instance.checkForRecomposition(interfaceInitializedArgs);
      });
    }

    return instance;
  }

  function getInterfaceDef(interfaceName) {
    var lcName = interfaceName.toLowerCase();
    // source may be null
    var kv = __objectFirst(__config.interfaceRegistry || {}, function (k, v) {
      return k.toLowerCase() === lcName;
    });
    if (!kv) {
      throw new Error("Unknown interface name: " + interfaceName);
    }
    return kv.value;
  }

  return __config;
})();

var __modelLibraryDef = __config.interfaceRegistry.modelLibrary;

// legacy
core.config = __config;

breeze.config = __config;;var observableArray = (function () {

  var mixin = {};
  mixin.push = function () {
    if (this._inProgress) {
      return -1;
    }

    var goodAdds = this._getGoodAdds(__arraySlice(arguments));
    if (!goodAdds.length) {
      return this.length;
    }
    this._beforeChange();
    var result = Array.prototype.push.apply(this, goodAdds);
    processAdds(this, goodAdds);
    return result;
  };

  mixin._push = function () {
    if (this._inProgress) {
      return -1;
    }
    var goodAdds = __arraySlice(arguments);
    this._beforeChange();
    var result = Array.prototype.push.apply(this, goodAdds);
    processAdds(this, goodAdds);
    return result;
  };

  mixin.unshift = function () {
    var goodAdds = this._getGoodAdds(__arraySlice(arguments));
    if (!goodAdds.length) {
      return this.length;
    }
    this._beforeChange();
    var result = Array.prototype.unshift.apply(this, goodAdds);
    processAdds(this, __arraySlice(goodAdds));
    return result;
  };

  mixin.pop = function () {
    this._beforeChange();
    var result = Array.prototype.pop.apply(this);
    processRemoves(this, [result]);
    return result;
  };

  mixin.shift = function () {
    this._beforeChange();
    var result = Array.prototype.shift.apply(this);
    processRemoves(this, [result]);
    return result;
  };

  mixin.splice = function () {
    var goodAdds = this._getGoodAdds(__arraySlice(arguments, 2));
    var newArgs = __arraySlice(arguments, 0, 2).concat(goodAdds);
    this._beforeChange();
    var result = Array.prototype.splice.apply(this, newArgs);
    processRemoves(this, result);

    if (goodAdds.length) {
      processAdds(this, goodAdds);
    }
    return result;
  };

  mixin.getEntityAspect = function () {
    return this.parent.entityAspect || this.parent.complexAspect.getEntityAspect();
  }

  mixin._getEventParent = function () {
    return this.getEntityAspect();
  };

  mixin._getPendingPubs = function () {
    var em = this.getEntityAspect().entityManager;
    return em && em._pendingPubs;
  };

  mixin._beforeChange = function () {
    // default is to do nothing
  };

  function updateEntityState(obsArray) {
    var entityAspect = obsArray.getEntityAspect();
    if (entityAspect.entityState.isUnchanged()) {
      entityAspect.setModified();
    }
    if (entityAspect.entityState.isModified() && !obsArray._origValues) {
      obsArray._origValues = obsArray.slice(0);
    }
  }

  function processAdds(obsArray, adds) {
    obsArray._processAdds(adds);
    // this is referencing the name of the method on the complexArray not the name of the event
    //var args = { added: adds };
    //args[obsArray._typeName] = obsArray;
    publish(obsArray, "arrayChanged", { array: obsArray, added: adds });
  }

  function processRemoves(obsArray, removes) {
    obsArray._processRemoves(removes);
    // this is referencing the name of the method on the array not the name of the event
    publish(obsArray, "arrayChanged", { array: obsArray, removed: removes });
  }

  function publish(publisher, eventName, eventArgs) {
    var pendingPubs = publisher._getPendingPubs();
    if (pendingPubs) {
      if (!publisher._pendingArgs) {
        publisher._pendingArgs = eventArgs;
        pendingPubs.push(function () {
          publisher[eventName].publish(publisher._pendingArgs);
          publisher._pendingArgs = null;
        });
      } else {
        combineArgs(publisher._pendingArgs, eventArgs);
      }
    } else {
      publisher[eventName].publish(eventArgs);
    }
  }

  function combineArgs(target, source) {
    for (var key in source) {
      if (key !== "array" && target.hasOwnProperty(key)) {
        var sourceValue = source[key];
        var targetValue = target[key];
        if (targetValue) {
          if (!Array.isArray(targetValue)) {
            throw new Error("Cannot combine non array args");
          }
          Array.prototype.push.apply(targetValue, sourceValue);
        } else {
          target[key] = sourceValue;
        }
      }
    }
  }

  function initializeParent(obsArray, parent, parentProperty) {
    obsArray.parent = parent;
    obsArray.parentProperty = parentProperty;
  }


  return {
    mixin: mixin,
    publish: publish,
    updateEntityState: updateEntityState,
    initializeParent: initializeParent
  };


})();;/**
@module breeze
**/

var Validator = (function () {

  var INT16_MIN = -32768;
  var INT16_MAX = 32767;

  var INT32_MIN = -2147483648;
  var INT32_MAX = 2147483647;

  var BYTE_MIN = 0;
  var BYTE_MAX = 255;

  // add common props and methods for every validator 'context' here.
  var rootContext = {
    displayName: function (context) {
      if (context.property) {
        return context.property.resolveProperty("displayName") || context.propertyName || context.property.name;
      } else {
        return "Value";
      }
    }
  };

  /**
  Instances of the Validator class provide the logic to validate another object and provide a description of any errors
  encountered during the validation process.  They are typically associated with a 'validators' property on the following types: {{#crossLink "EntityType"}}{{/crossLink}},
  {{#crossLink "DataProperty"}}{{/crossLink}} or {{#crossLink "NavigationProperty"}}{{/crossLink}}.

  A number of property level validators are registered automatically, i.e added to each DataProperty.validators property
  based on {{#crossLink "DataProperty"}}{{/crossLink}} metadata.  For example,

  - DataProperty.dataType -> one of the 'dataType' validator methods such as Validator.int64, Validator.date, Validator.bool etc.
  - DataProperty.maxLength -> Validator.maxLength
  - DataProperty.isNullable -> Validator.required (if not nullable)

  @class Validator
  **/

  /**
  Validator constructor - This method is used to create create custom validations.  Several
  basic "Validator" construction methods are also provided as static methods to this class. These methods
  provide a simpler syntax for creating basic validations.

  Many of these stock validators are inspired by and implemented to conform to the validators defined at
  http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx

  Sometimes a custom validator will be required.
  @example
  Most validators will be 'property' level validators, like this.
  @example
      // v is this function is the value to be validated, in this case a "country" string.
      var valFn = function (v) {
          if (v == null) return true;
          return (core.stringStartsWith(v, "US"));
      };
      var countryValidator = new Validator("countryIsUS", valFn, {
          displayName: "Country", 
          messageTemplate: "'%displayName%' must start with 'US'" 
      });

      // Now plug it into Breeze.
      // Assume em1 is a preexisting EntityManager.
      var custType = metadataStore.getEntityType("Customer");
      var countryProp = custType.getProperty("Country");
      // Note that validator is added to a 'DataProperty' validators collection.
      prop.validators.push(countryValidator);
  Entity level validators are also possible
  @example
      function isValidZipCode(value) {
          var re = /^\d{5}([\-]\d{4})?$/;
          return (re.test(value));
      }

      // v in this case will be a Customer entity
      var valFn = function (v) {
          // This validator only validates US Zip Codes.
          if ( v.getProperty("Country") === "USA") {
              var postalCode = v.getProperty("PostalCode");
              return isValidZipCode(postalCode);
          }
          return true;
      };
      var zipCodeValidator = new Validator("zipCodeValidator", valFn,
          { messageTemplate: "For the US, this is not a valid PostalCode" });

      // Now plug it into Breeze.
      // Assume em1 is a preexisting EntityManager.
      var custType = em1.metadataStore.getEntityType("Customer");
      // Note that validator is added to an 'EntityType' validators collection.
      custType.validators.push(zipCodeValidator);
  What is commonly needed is a way of creating a parameterized function that will itself
  return a new Validator.  This requires the use of a 'context' object.
  @example
      // create a function that will take in a config object
      // and will return a validator
      var numericRangeValidator = function(context) {
          var valFn = function(v, ctx) {
              if (v == null) return true;
              if (typeof(v) !== "number") return false;
              if (ctx.min != null && v < ctx.min) return false;
              if (ctx.max != null && v > ctx.max) return false;
              return true;
          };
          // The last parameter below is the 'context' object that will be passed into the 'ctx' parameter above
          // when this validator executes. Several other properties, such as displayName will get added to this object as well.
          return new Validator("numericRange", valFn, {
              messageTemplate: "'%displayName%' must be a number between the values of %min% and %max%",
              min: context.min,
              max: context.max
          });
      };
      // Assume that freightProperty is a DataEntityProperty that describes numeric values.
      // register the validator
      freightProperty.validators.push(numericRangeValidator({ min: 100, max: 500 }));

  Breeze substitutes context values and functions for the tokens in the messageTemplate when preparing the runtime error message;
  'displayName' is a pre-defined context function that is always available.

  Please note that Breeze substitutes the empty string for falsey parameters. That usually works in your favor.
  Sometimes it doesn't as when the 'min' value is zero in which case the message text would have a hole
  where the 'min' value goes, saying: "... an integer between the values of and ...". That is not what you want.

  To avoid this effect, you may can bake certain of the context values into the 'messageTemplate' itself
  as shown in this revision to the pertinent part of the previous example:
  @example
      // ... as before
      // ... but bake the min/max values into the message template.
      var template = breeze.core.formatString(
          "'%displayName%' must be a number between the values of %1 and %2",
          context.min, context.max);
      return new Validator("numericRange", valFn, {
          messageTemplate: template,
          min: context.min,
          max: context.max
      });

  @method <ctor> Validator
  @param name {String} The name of this validator.
  @param validatorFn {Function} A function to perform validation.

  validatorFn(value, context)
  @param validatorFn.value {Object} Value to be validated
  @param validatorFn.context {Object} The same context object passed into the constructor with the following additional properties if not
  otherwise specified.
  @param validatorFn.context.value {Object} The value being validated.
  @param validatorFn.context.name {String} The name of the validator being executed.
  @param validatorFn.context.displayName {String} This will be either the value of the property's 'displayName' property or
  the value of its 'name' property or the string 'Value'
  @param validatorFn.context.messageTemplate {String} This will either be the value of Validator.messageTemplates[ {this validators name}] or null. Validator.messageTemplates
  is an object that is keyed by validator name and that can be added to in order to 'register' your own message for a given validator.
  The following property can also be specified for any validator to force a specific errorMessage string
  @param [validatorFn.context.message] {String} If this property is set it will be used instead of the 'messageTemplate' property when an
  error message is generated.

  @param [context] {Object} A free form object whose properties will made available during the validation and error message creation process.
  This object will be passed into the Validator's validation function whenever 'validate' is called. See above for a description
  of additional properties that will be automatically added to this object if not otherwise specified.
  **/
  var ctor = function Validator(name, valFn, context) {
    // _baseContext is what will get serialized
    this._baseContext = context || {};
    this._baseContext.name = name;
    context = __extend(Object.create(rootContext), this._baseContext);
    context.messageTemplate = context.messageTemplate || ctor.messageTemplates[name];
    this.name = name;
    this.valFn = valFn;
    this.context = context;
  };
  var proto = ctor.prototype;
  proto._$typeName = "Validator";

  /**
  The name of this validator.

  __readOnly__
  @property name {String}
  **/

  /**
  The context for this validator.

  This object will typically contain at a minimum the following properties. "name", "displayName", and "message" or "messageTemplate".
  __readOnly__
  @property context {Object}
  **/


  /**
  Run this validator against the specified value.  This method will usually be called internally either
  automatically by an property change, entity attach, query or save operation, or manually as a result of
  a validateEntity call on the EntityAspect. The resulting ValidationResults are available via the
  EntityAspect.getValidationErrors method.

  However, you can also call a validator directly either for testing purposes or some other reason if needed.
  @example
      // using one of the predefined validators
      var validator = Validator.maxLength({ maxLength: 5, displayName: "City" });
      // should be ok because "asdf".length < 5
      var result = validator.validate("asdf");
      ok(result === null);
      result = validator.validate("adasdfasdf");
      // extract all of the properties of the 'result'
      var errMsg = result.errorMessage;
      var context = result.context;
      var sameValidator = result.validator;
  @method validate
  @param value {Object} Value to validate
  @param additionalContext {Object} Any additional contextual information that the Validator
  can make use of.
  @return {ValidationError|null} A ValidationError if validation fails, null otherwise
  **/
  proto.validate = function (value, additionalContext) {
    var currentContext;
    if (additionalContext) {
      currentContext = __extend(Object.create(this.context), additionalContext);
    } else {
      currentContext = this.context;
    }
    this.currentContext = currentContext;

    try {
      if (this.valFn(value, currentContext)) {
        return null;
      } else {
        currentContext.value = value;
        return new ValidationError(this, currentContext, this.getMessage());
      }
    } catch (e) {
      return new ValidationError(this, currentContext, "Exception occured while executing this validator: " + this.name);
    }
  };


  // context.value is not avail unless validate was called first.

  /**
  Returns the message generated by the most recent execution of this Validator.
  @example
      var v0 = Validator.maxLength({ maxLength: 5, displayName: "City" });
      v0.validate("adasdfasdf");
      var errMessage = v0.getMessage());
  @method getMessage
  @return {String}
  **/
  proto.getMessage = function () {
    try {
      var context = this.currentContext;
      var message = context.message;
      if (message) {
        if (typeof (message) === "function") {
          return message(context);
        } else {
          return message;
        }
      } else if (context.messageTemplate) {
        return formatTemplate(context.messageTemplate, context);
      } else {
        return "invalid value: " + (this.name || "{unnamed validator}");
      }
    } catch (e) {
      return "Unable to format error message" + e.toString();
    }
  };

  proto.toJSON = function () {
    return this._baseContext;
  };

  /**
  Creates a validator instance from a JSON object or an array of instances from an array of JSON objects.
  @method fromJSON
  @static
  @param json {Object} JSON object that represents the serialized version of a validator.
  **/
  ctor.fromJSON = function (json) {
    if (Array.isArray(json)) {
      return json.map(function (js) {
        return ctor.fromJSON(js);
      });
    }
    ;
    var validatorName = "Validator." + json.name;
    var fn = __config.getRegisteredFunction(validatorName);
    if (!fn) {
      throw new Error("Unable to locate a validator named:" + json.name);
    }
    return fn(json);
  };

  /**
  Register a validator instance so that any deserialized metadata can reference it.
  @method register
  @static
  @param validator {Validator} Validator to register.
  **/
  ctor.register = function (validator) {
    __config.registerFunction(function () {
      return validator;
    }, "Validator." + validator.name);
  };

  /**
  Register a validator factory so that any deserialized metadata can reference it.
  @method registerFactory
  @static
  @param validatorFactory {Function} A function that optionally takes a context property and returns a Validator instance.
  @param name {String} The name of the validator.
  **/
  ctor.registerFactory = function (validatorFn, name) {
    __config.registerFunction(validatorFn, "Validator." + name);
  };

  /**
  Map of standard error message templates keyed by validator name.
  You can add to or modify this object to customize the template used for any validation error message.
  @example
      // v is this function is the value to be validated, in this case a "country" string.
      var valFn = function (v) {
          if (v == null) return true;
          return (core.stringStartsWith(v, "US"));
      };
      var countryValidator = new Validator("countryIsUS", valFn, { displayName: "Country" });
      Validator.messageTemplates.countryIsUS = "'%displayName%' must start with 'US'";
      // This will have a similar effect to this
      var countryValidator = new Validator("countryIsUS", valFn, {
          displayName: "Country", 
          messageTemplate: "'%displayName%' must start with 'US'" 
      });
  @property messageTemplates {Object}
  @static
  **/
  ctor.messageTemplates = {
    bool: "'%displayName%' must be a 'true' or 'false' value",
    creditCard: "The %displayName% is not a valid credit card number",
    date: "'%displayName%' must be a date",
    duration: "'%displayName%' must be a ISO8601 duration string, such as 'P3H24M60S'",
    emailAddress: "The %displayName% '%value%' is not a valid email address",
    guid: "'%displayName%' must be a GUID",
    integer: "'%displayName%' must be an integer",
    integerRange: "'%displayName%' must be an integer between the values of %minValue% and %maxValue%",
    maxLength: "'%displayName%' must be a string with %maxLength% characters or less",
    number: "'%displayName%' must be a number",
    phone: "The %displayName% '%value%' is not a valid phone number",
    regularExpression: "The %displayName% '%value%' does not match '%expression%'",
    required: "'%displayName%' is required",
    string: "'%displayName%' must be a string",
    stringLength: "'%displayName%' must be a string with between %minLength% and %maxLength% characters",
    url: "The %displayName% '%value%' is not a valid url"
  };

  /**
  Returns a standard 'required value' Validator
  @example
      // Assume em1 is a preexisting EntityManager.
      var custType = em1.metadataStore.getEntityType("Customer");
      var regionProperty - custType.getProperty("Region");
      // Makes "Region" on Customer a required property.
      regionProperty.validators.push(Validator.required());
      // or to allow empty strings
      regionProperty.validators.push(Validator.required({ allowEmptyStrings: true }););
  @method required
  @static
  @param context {Object}
  @param [context.allowEmptyStrings] {Boolean} If this parameter is omitted or false then empty strings do NOT pass validation.
  @return {Validator} A new Validator
  **/
  ctor.required = function (context) {
    var valFn = function (v, ctx) {
      if (typeof v === "string") {
        if (ctx && ctx.allowEmptyStrings) return true;
        return v.length > 0;
      } else {
        return v != null;
      }
    };
    return new ctor("required", valFn, context);
  };

  /**
  Returns a standard maximum string length Validator; the maximum length must be specified
  @example
      // Assume em1 is a preexisting EntityManager.
      var custType = em1.metadataStore.getEntityType("Customer");
      var regionProperty - custType.getProperty("Region");
      // Validates that the value of the Region property on Customer will be less than or equal to 5 characters.
      regionProperty.validators.push(Validator.maxLength( {maxLength: 5}));
  @method maxLength
  @static
  @param context {Object}
  @param context.maxLength {Integer}
  @return {Validator} A new Validator
  **/
  ctor.maxLength = function (context) {
    var valFn = function (v, ctx) {
      if (v == null) return true;
      if (typeof (v) !== "string") return false;
      return v.length <= ctx.maxLength;
    };
    return new ctor("maxLength", valFn, context);
  };

  /**
  Returns a standard string length Validator; both minimum and maximum lengths must be specified.
  @example
      // Assume em1 is a preexisting EntityManager.
      var custType = em1.metadataStore.getEntityType("Customer");
      var regionProperty - custType.getProperty("Region");
      // Validates that the value of the Region property on Customer will be
      // between 2 and 5 characters
      regionProperty.validators.push(Validator.stringLength( {minLength: 2, maxLength: 5});
  @method stringLength
  @static
  @param context {Object}
  @param context.maxLength {Integer}
  @param context.minLength {Integer}
  @return {Validator} A new Validator
  **/
  ctor.stringLength = function (context) {
    var valFn = function (v, ctx) {
      if (v == null) return true;
      if (typeof (v) !== "string") return false;
      if (ctx.minLength != null && v.length < ctx.minLength) return false;
      if (ctx.maxLength != null && v.length > ctx.maxLength) return false;
      return true;
    };
    return new ctor("stringLength", valFn, context);
  };

  /**
  Returns a standard string dataType Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var custType = em1.metadataStore.getEntityType("Customer");
      var regionProperty - custType.getProperty("Region");
      // Validates that the value of the Region property on Customer is a string.
      regionProperty.validators.push(Validator.string());
  @method string
  @static
  @return {Validator} A new Validator
  **/
  ctor.string = function () {
    var valFn = function (v) {
      if (v == null) return true;
      return (typeof v === "string");
    };
    return new ctor("string", valFn);
  };

  /**
  Returns a Guid data type Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var custType = em1.metadataStore.getEntityType("Customer");
      var customerIdProperty - custType.getProperty("CustomerID");
      // Validates that the value of the CustomerID property on Customer is a Guid.
      customerIdProperty.validators.push(Validator.guid());
  @method guid
  @static
  @return {Validator} A new Validator
  **/
  ctor.guid = function () {
    var valFn = function (v) {
      if (v == null) return true;
      return __isGuid(v);
    };
    return new ctor("guid", valFn);
  };

  /**
  Returns a ISO 8601 duration string  Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var eventType = em1.metadataStore.getEntityType("Event");
      var elapsedTimeProperty - eventType.getProperty("ElapsedTime");
      // Validates that the value of the ElapsedTime property on Customer is a duration.
      elapsedTimeProperty.validators.push(Validator.duration());
  @method duration
  @static
  @return {Validator} A new Validator
  **/
  ctor.duration = function () {
    var valFn = function (v) {
      if (v == null) return true;
      return __isDuration(v);
    };
    return new ctor("duration", valFn);
  };

  /**
  Returns a standard numeric data type Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var orderType = em1.metadataStore.getEntityType("Order");
      var freightProperty - orderType.getProperty("Freight");
      // Validates that the value of the Freight property on Order is a number.
      freightProperty.validators.push(Validator.number());
  @method number
  @static
  @return {Validator} A new Validator
  **/

    // TODO: may need to have seperate logic for single.
  ctor.number = ctor.double = ctor.single = function (context) {
    var valFn = function (v, ctx) {
      if (v == null) return true;
      if (typeof v === "string" && ctx && ctx.allowString) {
        v = parseFloat(v, 10);
      }
      return (typeof v === "number" && !isNaN(v));
    };
    return new ctor("number", valFn, context);
  };

  /**
  Returns a standard large integer data type - 64 bit - Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var orderType = em1.metadataStore.getEntityType("Order");
      var freightProperty - orderType.getProperty("Freight");
      // Validates that the value of the Freight property on Order is within the range of a 64 bit integer.
      freightProperty.validators.push(Validator.int64());
  @method int64
  @static
  @return {Validator} A new Validator
  **/
  ctor.integer = ctor.int64 = function (context) {
    var valFn = function (v, ctx) {
      if (v == null) return true;
      if (typeof v === "string" && ctx && ctx.allowString) {
        v = parseInt(v, 10);
      }
      return (typeof v === "number") && (!isNaN(v)) && Math.floor(v) === v;
    };
    return new ctor("integer", valFn, context);
  };

  /**
  Returns a standard 32 bit integer data type Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var orderType = em1.metadataStore.getEntityType("Order");
      var freightProperty - orderType.getProperty("Freight");
      freightProperty.validators.push(Validator.int32());
  @method int32
  @static
  @return {Validator} A new Validator
  **/
  ctor.int32 = function (context) {
    return intRangeValidatorCtor("int32", INT32_MIN, INT32_MAX, context)();
  };

  /**
  Returns a standard 16 bit integer data type Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var orderType = em1.metadataStore.getEntityType("Order");
      var freightProperty - orderType.getProperty("Freight");
      // Validates that the value of the Freight property on Order is within the range of a 16 bit integer.
      freightProperty.validators.push(Validator.int16());
  @method int16
  @static
  @return {Validator} A new Validator
  **/
  ctor.int16 = function (context) {
    return intRangeValidatorCtor("int16", INT16_MIN, INT16_MAX, context)();
  };

  /**
  Returns a standard byte data type Validator. (This is a integer between 0 and 255 inclusive for js purposes).
  @example
      // Assume em1 is a preexisting EntityManager.
      var orderType = em1.metadataStore.getEntityType("Order");
      var freightProperty - orderType.getProperty("Freight");
      // Validates that the value of the Freight property on Order is within the range of a 16 bit integer.
      // Probably not a very good validation to place on the Freight property.
      regionProperty.validators.push(Validator.byte());
  @method byte
  @static
  @return {Validator} A new Validator
  **/
  ctor.byte = function (context) {
    return intRangeValidatorCtor("byte", BYTE_MIN, BYTE_MAX, context)();
  };

  /**
  Returns a standard boolean data type Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var productType = em1.metadataStore.getEntityType("Product");
      var discontinuedProperty - productType.getProperty("Discontinued");
      // Validates that the value of the Discontinued property on Product is a boolean
      discontinuedProperty.validators.push(Validator.bool());
  @method bool
  @static
  @return {Validator} A new Validator
  **/
  ctor.bool = function () {
    var valFn = function (v) {
      if (v == null) return true;
      return (v === true) || (v === false);
    };
    return new ctor("bool", valFn);
  };

  ctor.none = function () {
    var valFn = function (v) {
      return true;
    };
    return new ctor("none", valFn);
  };

  /**
  Returns a standard date data type Validator.
  @example
      // Assume em1 is a preexisting EntityManager.
      var orderType = em1.metadataStore.getEntityType("Order");
      var orderDateProperty - orderType.getProperty("OrderDate");
      // Validates that the value of the OrderDate property on Order is a date
      // Probably not a very good validation to place on the Freight property.
      orderDateProperty.validators.push(Validator.date());
  @method date
  @static
  @return {Validator} A new Validator
  **/
  ctor.date = function () {
    var valFn = function (v) {
      if (v == null) return true;
      if (typeof v === "string") {
        try {
          return !isNaN(Date.parse(v));
          // old code
          // return __isDate(new Date(v));
        } catch (e) {
          return false;
        }
      } else {
        return __isDate(v);
      }
    };
    return new ctor("date", valFn);
  };

  /**
  Returns a credit card number validator
  Performs a luhn algorithm checksum test for plausability
  catches simple mistakes; only service knows for sure
  @example
      // Assume em is a preexisting EntityManager.
      var personType = em.metadataStore.getEntityType("Person");
      var creditCardProperty = personType.getProperty("creditCard");
      // Validates that the value of the Person.creditCard property is credit card.
      creditCardProperty.validators.push(Validator.creditCard());
  @method creditCard
  @static
  @param [context] {Object} optional parameters to pass through to validation constructor
  @return {Validator} A new Validator
  **/
  ctor.creditCard = function (context) {
    function valFn(v) {
      if (v == null || v === '') return true;
      if (typeof (v) !== 'string') return false;
      v = v.replace(/(\-|\s)/g, ""); // remove dashes and spaces
      if (!v || /\D/.test(v)) return false; // all digits, not empty
      return luhn(v);
    };
    return new ctor('creditCard', valFn, context);
  };

  // http://rosettacode.org/wiki/Luhn_test_of_credit_card_numbers#JavaScript
  function luhn(a, b, c, d, e) {
    for (d = +a[b = a.length - 1], e = 0; b--;)
      c = +a[b], d += ++e % 2 ? 2 * c % 10 + (c > 4) : c;
    return !(d % 10);
  };

  /**
  Returns a regular expression validator; the expression must be specified
  @example
      // Add validator to a property. Assume em is a preexisting EntityManager.
      var customerType = em.metadataStore.getEntityType("Customer");
      var regionProperty = customerType.getProperty("Region");
      // Validates that the value of Customer.Region is 2 char uppercase alpha.
      regionProperty.validators.push(Validator.regularExpression( {expression: '^[A-Z]{2}$'} );
  @method regularExpression
  @static
  @param context {Object}
  @param context.expression {String} String form of the regular expression to apply
  @return {Validator} A new Validator
  **/
  ctor.regularExpression = function (context) {
    function valFn(v, ctx) {
      // do not invalidate if empty; use a separate required test
      if (v == null || v === '') return true;
      if (typeof (v) !== 'string') return false;
      try {
        var re = new RegExp(ctx.expression);
      } catch (e) {
        throw new Error('Missing or invalid expression parameter to regExp validator');
      }
      return re.test(v);
    };
    return new ctor('regularExpression', valFn, context);
  };

  /**
  Returns the email address validator
  @example
      // Assume em is a preexisting EntityManager.
      var personType = em.metadataStore.getEntityType("Person");
      var emailProperty = personType.getProperty("email");
      // Validates that the value of the Person.email property is an email address.
      emailProperty.validators.push(Validator.emailAddress());
  @method emailAddress
  @static
  @param [context] {Object} optional parameters to pass through to validation constructor
  @return {Validator} A new Validator
  **/
  ctor.emailAddress = function (context) {
    // See https://github.com/srkirkland/DataAnnotationsExtensions/blob/master/DataAnnotationsExtensions/EmailAttribute.cs
    var reEmailAddress = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i;
    return makeRegExpValidator('emailAddress', reEmailAddress, null, context);
  };

  /**
  Returns the phone validator
  Provides basic assertions on the format and will help to eliminate most nonsense input
  Matches:
  International dialing prefix: {{}, +, 0, 0000} (with or without a trailing break character, if not '+': [-/. ])
  > ((\+)|(0(\d+)?[-/.\s]))
  Country code: {{}, 1, ..., 999} (with or without a trailing break character: [-/. ])
  > [1-9]\d{,2}[-/.\s]?
  Area code: {(0), ..., (000000), 0, ..., 000000} (with or without a trailing break character: [-/. ])
  > ((\(\d{1,6}\)|\d{1,6})[-/.\s]?)?
  Local: {0, ...}+ (with or without a trailing break character: [-/. ])
  > (\d+[-/.\s]?)+\d+
  @example
      // Assume em is a preexisting EntityManager.
      var customerType = em.metadataStore.getEntityType("Customer");
      var phoneProperty = customerType.getProperty("phone");
      // Validates that the value of the Customer.phone property is phone.
      phoneProperty.validators.push(Validator.phone());
  @method phone
  @static
  @param [context] {Object} optional parameters to pass through to validation constructor
  @return {Validator} A new Validator
  **/
  ctor.phone = function (context) {
    // See https://github.com/srkirkland/DataAnnotationsExtensions/blob/master/DataAnnotationsExtensions/Expressions.cs
    var rePhone = /^((\+|(0(\d+)?[-/.\s]?))[1-9]\d{0,2}[-/.\s]?)?((\(\d{1,6}\)|\d{1,6})[-/.\s]?)?(\d+[-/.\s]?)+\d+$/;
    return makeRegExpValidator('phone', rePhone, null, context);
  };

  /**
  Returns the URL (protocol required) validator
  @example
      // Assume em is a preexisting EntityManager.
      var personType = em.metadataStore.getEntityType("Person");
      var websiteProperty = personType.getProperty("website");
      // Validates that the value of the Person.website property is a URL.
      websiteProperty.validators.push(Validator.url());
  @method url
  @static
  @param [context] {Object} optional parameters to pass through to validation constructor
  @return {Validator} A new Validator
  **/
  ctor.url = function (context) {
    //See https://github.com/srkirkland/DataAnnotationsExtensions/blob/master/DataAnnotationsExtensions/UrlAttribute.cs
    var reUrlProtocolRequired = /^(https?|ftp):\/\/(((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-fA-F]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|([a-zA-Z][\-a-zA-Z0-9]*)|((([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-zA-Z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-fA-F]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-fA-F]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-fA-F]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-zA-Z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-fA-F]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/;
    return makeRegExpValidator('url', reUrlProtocolRequired, null, context);
  };

  /**
  Creates a regular expression validator with a fixed expression.
  Many of the stock validators are built with this factory method.
  Their expressions are often derived from
  https://github.com/srkirkland/DataAnnotationsExtensions/blob/master/DataAnnotationsExtensions
  You can try many of them at http://dataannotationsextensions.org/
  @example
      // Make a zipcode validator
      function zipValidator = Validator.makeRegExpValidator(
      "zipVal,
      /^\d{5}([\-]\d{4})?$/,
      "The %displayName% '%value%' is not a valid U.S. zipcode");
      // Register it.
      Validator.register(zipValidator);
      // Add it to a data property. Assume em is a preexisting EntityManager.
      var custType = em.metadataStore.getEntityType("Customer");
      var zipProperty = custType.getProperty("PostalCode");
      zipProperty.validators.push(zipValidator);
  @method makeRegExpValidator
  @static
  @param validatorName {String} name of this validator
  @param expression {String | RegExp} regular expression to apply
  @param [defaultMessage] {String} default message for failed validations
  @param [context] {Object} optional parameters to pass through to validation constructor
  @return {Validator} A new Validator
  **/
  ctor.makeRegExpValidator = makeRegExpValidator;

  function makeRegExpValidator(validatorName, expression, defaultMessage, context) {
    if (defaultMessage) {
      ctor.messageTemplates[validatorName] = defaultMessage;
    }
    var re = (typeof (expression) === 'string') ? new RegExp(expression) : expression;
    var valFn = function (v) {
      // do not invalidate if empty; use a separate required test
      if (v == null || v === '') return true;
      if (typeof (v) !== 'string') return false;
      return re.test(v);
    };
    return new ctor(validatorName, valFn, context);
  };

  // register all validators
  __objectForEach(ctor, function (key, value) {
    if (typeof (value) !== "function") {
      return;
    }
    if (key === "fromJSON" || key === "register" ||
        key === "registerFactory" || key === "makeRegExpValidator") {
      return;
    }

    __config.registerFunction(value, "Validator." + key);
  });


  // private funcs

  function formatTemplate(template, vars, ownPropertiesOnly) {
    if (!vars) return template;
    return template.replace(/%([^%]+)%/g, function (_, key) {
      var valOrFn;
      if (ownPropertiesOnly) {
        valOrFn = vars.hasOwnProperty(key) ? vars[key] : '';
      } else {
        valOrFn = vars[key];
      }
      if (valOrFn != null) {
        if (__isFunction(valOrFn)) {
          return valOrFn(vars);
        } else {
          return valOrFn;
        }
      } else {
        return "";
      }
    });
  }

  function intRangeValidatorCtor(validatorName, minValue, maxValue, context) {
    context = context || {};
    if (minValue !== undefined) { context.min = minValue; }
    if (maxValue !== undefined) { context.max = maxValue; }
    var templateExists = context.messageTemplate || ctor.messageTemplates[validatorName];
    if (!templateExists) {
      ctor.messageTemplates[validatorName] = __formatString("'%displayName%' must be an integer between the values of %1 and %2",
          minValue, maxValue);
    }
    return function () {
      var valFn = function (v, ctx) {
        if (v == null) return true;
        if (typeof v === "string" && ctx && ctx.allowString) {
          v = parseInt(v, 0);
        }
        if ((typeof v === "number") && (!isNaN(v)) && Math.floor(v) === v) {
          if (minValue != null && v < minValue) {
            return false;
          }
          if (maxValue != null && v > maxValue) {
            return false;
          }
          return true;
        } else {
          return false;
        }
      };
      return new ctor(validatorName, valFn, context);
    };
  }


  return ctor;
})();

var ValidationError = (function () {
  /**
  A ValidationError is used to describe a failed validation.

  @class ValidationError
  **/

  /**
  Constructs a new ValidationError
  @method <ctor> ValidationError

  @param validator {Validator || null} The Validator used to create this error, if any.
  @param context { ContextObject || null} The Context object used in conjunction with the Validator to create this error.
  @param errorMessage { String} The actual error message
  @param [key] {String} An optional key used to define a key for this error. One will be created automatically if not provided here.
  **/
  var ctor = function ValidationError(validator, context, errorMessage, key) {
    assertParam(validator, "validator").isOptional().isInstanceOf(Validator).check();
    assertParam(errorMessage, "errorMessage").isNonEmptyString().check();
    assertParam(key, "key").isOptional().isNonEmptyString().check();
    this.validator = validator;
    context = context || {};
    this.context = context;
    this.errorMessage = errorMessage;

    this.property = context.property;
    this.propertyName = context.propertyName || (context.property && context.property.name);

    if (key) {
      this.key = key;
    } else {
      this.key = ValidationError.getKey(validator || errorMessage, this.propertyName);
    }
    this.isServerError = false;
  };


  /**
  The Validator associated with this ValidationError.

  __readOnly__
  @property validator {Validator}
  **/

  /**
  A 'context' object associated with this ValidationError.

  __readOnly__
  @property context {Object}
  **/

  /**
  The DataProperty or NavigationProperty associated with this ValidationError.

  __readOnly__
  @property property {DataProperty|NavigationProperty}
  **/

  /**
  The property name associated with this ValidationError. This will be a "property path" for any properties of a complex object.

  __readOnly__
  @property propertyName {String}
  **/

  /**
  The error message associated with the ValidationError.

  __readOnly__
  @property errorMessage {string}
  **/

  /**
  The key by which this validation error may be removed from a collection of ValidationErrors.

  __readOnly__
  @property key {string}
  **/

  /**
  Whether this is a server error.

  __readOnly__
  @property isServerError {bool}
  **/


  /**
  Composes a ValidationError 'key' given a validator or an errorName and an optional propertyName
  @method getKey
  @static
  @param validator {ValidatorOrErrorKey} A Validator or an "error name" if no validator is available.
  @param [propertyName] A property name
  @return {String} A ValidationError 'key'
  **/
  ctor.getKey = function (validatorOrErrorName, propertyName) {
    return (validatorOrErrorName.name || validatorOrErrorName) + (propertyName ? ":" + propertyName : "");
  };


  return ctor;
})();

breeze.Validator = Validator;
breeze.ValidationError = ValidationError;
 
;/**
@module breeze
**/

var ValidationOptions = (function () {

  /**
  A ValidationOptions instance is used to specify the conditions under which validation will be executed.

  @class ValidationOptions
  **/

  /**
  ValidationOptions constructor
  @example
      var newVo = new ValidationOptions( { validateOnSave: false, validateOnAttach: false });
      // assume em1 is a preexisting EntityManager
      em1.setProperties( { validationOptions: newVo });
  @method <ctor> ValidationOptions
  @param [config] {Object}
  @param [config.validateOnAttach=true] {Boolean}
  @param [config.validateOnSave=true] {Boolean}
  @param [config.validateOnQuery=false] {Boolean}
  @param [config.validateOnPropertyChange=true] {Boolean}
  **/
  var ctor = function ValidationOptions(config) {
    updateWithConfig(this, config);
  };
  var proto = ctor.prototype;

  /**
  Whether entity and property level validation should occur when entities are attached to the EntityManager other than via a query.

  __readOnly__
  @property validateOnAttach {Boolean}
  **/

  /**
  Whether entity and property level validation should occur before entities are saved. A failed validation will force the save to fail early.

  __readOnly__
  @property validateOnSave {Boolean}
  **/

  /**
  Whether entity and property level validation should occur after entities are queried from a remote server.

  __readOnly__
  @property validateOnQuery {Boolean}
  **/

  /**
  Whether property level validation should occur after entities are modified.

  __readOnly__
  @property validateOnPropertyChange {Boolean}
  **/

  proto._$typeName = "ValidationOptions";

  /**
  Returns a copy of this ValidationOptions with changes to the specified config properties.
  @example
      var validationOptions = new ValidationOptions();
      var newOptions = validationOptions.using( { validateOnQuery: true, validateOnSave: false} );
  @method using
  @param config {Object} The object to apply to create a new QueryOptions.
  @param [config.validateOnAttach] {Boolean}
  @param [config.validateOnSave] {Boolean}
  @param [config.validateOnQuery] {Boolean}
  @param [config.validateOnPropertyChange] {Boolean}
  @return {ValidationOptions}
  @chainable
  **/
  proto.using = function (config) {
    if (!config) return this;
    var result = new ValidationOptions(this);
    updateWithConfig(result, config);
    return result;
  };

  /**
  Sets the 'defaultInstance' by creating a copy of the current 'defaultInstance' and then applying all of the properties of the current instance.
  The current instance is returned unchanged.
  @example
      var validationOptions = new ValidationOptions()
      var newOptions = validationOptions.using( { validateOnQuery: true, validateOnSave: false} );
      var newOptions.setAsDefault();
  @method setAsDefault
  @chainable
  **/
  proto.setAsDefault = function () {
    return __setAsDefault(this, ctor);
  };

  /**
  The default value whenever ValidationOptions are not specified.
  @property defaultInstance {ValidationOptions}
  @static
  **/
  ctor.defaultInstance = new ctor({
    validateOnAttach: true,
    validateOnSave: true,
    validateOnQuery: false,
    validateOnPropertyChange: true
  });

  function updateWithConfig(obj, config) {
    if (config) {
      assertConfig(config)
          .whereParam("validateOnAttach").isBoolean().isOptional()
          .whereParam("validateOnSave").isBoolean().isOptional()
          .whereParam("validateOnQuery").isBoolean().isOptional()
          .whereParam("validateOnPropertyChange").isBoolean().isOptional()
          .applyAll(obj);
    }
    return obj;
  }

  return ctor;
})();

// expose

breeze.ValidationOptions = ValidationOptions;



;breeze.makeComplexArray = (function () {
  var complexArrayMixin = {};

  // complexArray will have the following props
  //    parent
  //    propertyPath
  //    parentProperty
  //    addedItems  - only if modified
  //    removedItems  - only if modified
  //  each complexAspect of any entity within a complexArray
  //  will have its own _complexState = "A/M";

  /**
   Complex arrays are not actually classes, they are objects that mimic arrays. A complex array is collection of
   complexTypes associated with a data property on a single entity or other complex object. i.e. customer.orders or order.orderDetails.
   This collection looks like an array in that the basic methods on arrays such as 'push', 'pop', 'shift', 'unshift', 'splice'
   are all provided as well as several special purpose methods.
   @class {complexArray}
   **/

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever the contents of this array changed.  This event
  is fired any time a new entity is attached or added to the EntityManager and happens to belong to this collection.
  Adds that occur as a result of query or import operations are batched so that all of the adds or removes to any individual
  collections are collected into a single notification event for each relation array.
  @example
      // assume order is an order entity attached to an EntityManager.
      orders.arrayChanged.subscribe(
      function (arrayChangedArgs) {
          var addedEntities = arrayChangedArgs.added;
          var removedEntities = arrayChanged.removed;
      });
  @event arrayChanged
  @param added {Array of Entity} An array of all of the entities added to this collection.
  @param removed {Array of Entity} An array of all of the removed from this collection.
  @readOnly
  **/

    // virtual impls
  complexArrayMixin._getGoodAdds = function (adds) {
    return getGoodAdds(this, adds);
  };

  complexArrayMixin._beforeChange = function () {
    observableArray.updateEntityState(this);
  };

  complexArrayMixin._processAdds = function (adds) {
    processAdds(this, adds);
  };

  complexArrayMixin._processRemoves = function (removes) {
    processRemoves(this, removes);
  };
  //

  complexArrayMixin._rejectChanges = function() {
    if (!this._origValues) return;
    var that = this;
    this.forEach(function (co) {
      clearAspect(co, that);
    });
    this.length = 0;
    this._origValues.forEach(function (co) {
      that.push(co);
    });
  };

  complexArrayMixin._acceptChanges = function () {
    this._origValues = null;
  };

  // local functions


  function getGoodAdds(complexArray, adds) {
    // remove any that are already added here
    return adds.filter(function (a) {
      return a.parent !== complexArray.parent;
    });
  }

  function processAdds(complexArray, adds) {
    adds.forEach(function (a) {
      if (a.parent != null) {
        throw new Error("The complexObject is already attached. Either clone it or remove it from its current owner");
      }
      setAspect(a, complexArray);
    });
  }

  function processRemoves(complexArray, removes) {
    removes.forEach(function (a) {
      clearAspect(a, complexArray);
    });
  }

  function clearAspect(co, arr) {
    var coAspect = co.complexAspect;
    // if not already attached - exit
    if (coAspect.parent !== arr.parent) return null;

    coAspect.parent = null;
    coAspect.parentProperty = null;
    return coAspect;
  }

  function setAspect(co, arr) {
    var coAspect = co.complexAspect;
    // if already attached - exit
    if (coAspect.parent === arr.parent) return null;
    coAspect.parent = arr.parent;
    coAspect.parentProperty = arr.parentProperty;

    return coAspect;
  }

  function makeComplexArray(arr, parent, parentProperty) {

    observableArray.initializeParent(arr, parent, parentProperty);
    arr.arrayChanged = new Event("arrayChanged", arr);
    __extend(arr, observableArray.mixin);
    return __extend(arr, complexArrayMixin);
  }

  return makeComplexArray;
})();;/**
@module breeze
**/


var EntityAction = (function () {
  /**
  EntityAction is an 'Enum' containing all of the valid actions that can occur to an 'Entity'.

  @class EntityAction
  @static
  **/
  var entityActionMethods = {
    isAttach: function () {
      return !!this.isAttach;
    },
    isDetach: function () {
      return !!this.isDetach;
    },
    isModification: function () {
      return !!this.isModification;
    }
  };

  var EntityAction = new Enum("EntityAction", entityActionMethods);

  /**
  Attach - Entity was attached via an AttachEntity call.

  @property Attach {EntityAction}
  @final
  @static
  **/
  EntityAction.Attach = EntityAction.addSymbol({ isAttach: true});

  /**
  AttachOnQuery - Entity was attached as a result of a query.

  @property AttachOnQuery {EntityAction}
  @final
  @static
  **/
  EntityAction.AttachOnQuery = EntityAction.addSymbol({ isAttach: true});

  /**
  AttachOnImport - Entity was attached as a result of an import.

  @property AttachOnImport {EntityAction}
  @final
  @static
  **/
  EntityAction.AttachOnImport = EntityAction.addSymbol({ isAttach: true});


  /**
  Detach - Entity was detached.

  @property Detach {EntityAction}
  @final
  @static
  **/
  EntityAction.Detach = EntityAction.addSymbol({ isDetach: true });

  /**
  MergeOnQuery - Properties on the entity were merged as a result of a query.

  @property MergeOnQuery {EntityAction}
  @final
  @static
  **/
  EntityAction.MergeOnQuery = EntityAction.addSymbol({ isModification: true });

  /**
  MergeOnImport - Properties on the entity were merged as a result of an import.

  @property MergeOnImport {EntityAction}
  @final
  @static
  **/
  EntityAction.MergeOnImport = EntityAction.addSymbol({ isModification: true });

  /**
  MergeOnSave - Properties on the entity were merged as a result of a save

  @property MergeOnSave {EntityAction}
  @final
  @static
  **/
  EntityAction.MergeOnSave = EntityAction.addSymbol({ isModification: true });

  /**
  PropertyChange - A property on the entity was changed.

  @property PropertyChange {EntityAction}
  @final
  @static
  **/
  EntityAction.PropertyChange = EntityAction.addSymbol({ isModification: true});

  /**
  EntityStateChange - The EntityState of the entity was changed.

  @property EntityStateChange {EntityAction}
  @final
  @static
  **/
  EntityAction.EntityStateChange = EntityAction.addSymbol();


  /**
  AcceptChanges - AcceptChanges was called on the entity, or its entityState was set to Unmodified.

  @property AcceptChanges {EntityAction}
  @final
  @static
  **/
  EntityAction.AcceptChanges = EntityAction.addSymbol();

  /**
  RejectChanges - RejectChanges was called on the entity.

  @property RejectChanges {EntityAction}
  @final
  @static
  **/
  EntityAction.RejectChanges = EntityAction.addSymbol({ isModification: true});

  /**
  Clear - The EntityManager was cleared.  All entities detached.

  @property Clear {EntityAction}
  @final
  @static
  **/
  EntityAction.Clear = EntityAction.addSymbol({ isDetach: true});

  EntityAction.resolveSymbols();
  return EntityAction;
})();

breeze.EntityAction = EntityAction;

;/**
@module breeze
**/

var EntityAspect = (function () {
  /**
  An EntityAspect instance is associated with every attached entity and is accessed via the entity's 'entityAspect' property.

  The EntityAspect itself provides properties to determine and modify the EntityState of the entity and has methods
  that provide a variety of services including validation and change tracking.

  An EntityAspect will almost never need to be constructed directly. You will usually get an EntityAspect by accessing
  an entities 'entityAspect' property.  This property will be automatically attached when an entity is created via either
  a query, import or EntityManager.createEntity call.
  @example
      // assume order is an order entity attached to an EntityManager.
      var aspect = order.entityAspect;
      var currentState = aspect.entityState;
  @class EntityAspect
  **/
  var ctor = function EntityAspect(entity) {
    if (entity === null) {
      var nullInstance = EntityAspect._nullInstance;
      if (nullInstance) return nullInstance;
      EntityAspect._nullInstance = this;
    } else if (entity === undefined) {
      throw new Error("The EntityAspect ctor requires an entity as its only argument.");
    } else if (entity.entityAspect) {
      return entity.entityAspect;
    }

    // if called without new
    if (!(this instanceof EntityAspect)) {
      return new EntityAspect(entity);
    }

    this.entity = entity;
    // TODO: keep public or not?
    this.entityGroup = null;
    this.entityManager = null;
    this.entityState = EntityState.Detached;
    this.isBeingSaved = false;
    this.originalValues = {};
    this.hasValidationErrors = false;
    this._validationErrors = {};

    // Uncomment when we implement entityAspect.isNavigationPropertyLoaded method
    // this._loadedNavPropMap = {};

    this.validationErrorsChanged = new Event("validationErrorsChanged", this);
    this.propertyChanged = new Event("propertyChanged", this);
    // in case this is the NULL entityAspect. - used with ComplexAspects that have no parent.

    if (entity != null) {
      entity.entityAspect = this;
      // entityType should already be on the entity from 'watch'
      var entityType = entity.entityType || entity._$entityType;
      if (!entityType) {
        var typeName = entity.prototype._$typeName;
        if (!typeName) {
          throw new Error("This entity is not registered as a valid EntityType");
        } else {
          throw new Error("Metadata for this entityType has not yet been resolved: " + typeName);
        }
      }
      var entityCtor = entityType.getEntityCtor();
      __modelLibraryDef.getDefaultInstance().startTracking(entity, entityCtor.prototype);
    }
  };
  var proto = ctor.prototype;


  Event.bubbleEvent(proto, function () {
    return this.entityManager;
  });

  /**
  The Entity that this aspect is associated with.

  __readOnly__
  @property entity {Entity}
  **/

  /**
  The {{#crossLink "EntityManager"}}{{/crossLink}} that contains this entity.

  __readOnly__
  @property entityManager {EntityManager}
  **/

  /**
  The {{#crossLink "EntityState"}}{{/crossLink}} of this entity.

  __readOnly__
  @property entityState {EntityState}
  **/

  /**
  Extra metadata about this entity such as the entity's etag.
  You may extend this object with your own metadata information.
  Breeze (de)serializes this object when importing/exporting the entity.

  @property extraMetadata {Object}
  **/

  /**
  Whether this entity is in the process of being saved.

  __readOnly__
  @property isBeingSaved {Boolean}
  **/

  /**
  Whether this entity has any validation errors.

  __readOnly__
  @property hasValidationErrors {Boolean}
  **/

  /**
  The 'original values' of this entity where they are different from the 'current values'.
  This is a map where the key is a property name and the value is the 'original value' of the property.

  __readOnly__
  @property originalValues {Object}
  **/

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever a value of one of this entity's properties change.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.propertyChanged.subscribe(
      function (propertyChangedArgs) {
          // this code will be executed anytime a property value changes on the 'order' entity.
          var entity = propertyChangedArgs.entity; // Note: entity === order
          var propertyNameChanged = propertyChangedArgs.propertyName;
          var oldValue = propertyChangedArgs.oldValue;
          var newValue = propertyChangedArgs.newValue;
      });
  @event propertyChanged
  @param entity {Entity} The entity whose property has changed.
  @param property {DataProperty} The DataProperty that changed.
  @param propertyName {String} The name of the property that changed. This value will be 'null' for operations that replace the entire entity.  This includes
  queries, imports and saves that require a merge. The remaining parameters will not exist in this case either. This will actually be a "property path"
  for any properties of a complex type.
  @param oldValue {Object} The old value of this property before the change.
  @param newValue {Object} The new value of this property after the change.
  @param parent {Object} The immediate parent object for the changed property.  This will be different from the 'entity' for any complex type or nested complex type properties.
  @readOnly
  **/

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever any of the validation errors on this entity change.
  Note that this might be the removal of an error when some data on the entity is fixed.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.validationErrorsChanged.subscribe(
      function (validationChangeArgs) {
          // this code will be executed anytime a property value changes on the 'order' entity.
          var entity == validationChangeArgs.entity; // Note: entity === order
          var errorsAdded = validationChangeArgs.added;
          var errorsCleared = validationChangeArgs.removed;
      });
  @event validationErrorsChanged
  @param entity {Entity} The entity on which the validation errors are being added or removed.
  @param added {Array of ValidationError} An array containing any newly added {{#crossLink "ValidationError"}}{{/crossLink}}s
  @param removed {Array of ValidationError} An array containing any newly removed {{#crossLink "ValidationError"}}{{/crossLink}}s. This is those
  errors that have been 'fixed'
  @readOnly
  **/

  /**
  Returns the {{#crossLink "EntityKey"}}{{/crossLink}} for this Entity.
  @example
      // assume order is an order entity attached to an EntityManager.
      var entityKey = order.entityAspect.getKey();
  @method getKey
  @param [forceRefresh=false] {Boolean} Forces the recalculation of the key.  This should normally be unnecessary.
  @return {EntityKey} The {{#crossLink "EntityKey"}}{{/crossLink}} associated with this Entity.
  **/
  proto.getKey = function (forceRefresh) {
    forceRefresh = assertParam(forceRefresh, "forceRefresh").isBoolean().isOptional().check(false);
    if (forceRefresh || !this._entityKey) {
      var entityType = this.entity.entityType;
      var keyProps = entityType.keyProperties;
      var values = keyProps.map(function (p) {
        return this.entity.getProperty(p.name);
      }, this);
      this._entityKey = new EntityKey(entityType, values);
    }
    return this._entityKey;
  };

  /**
  Returns the entity to an {{#crossLink "EntityState"}}{{/crossLink}} of 'Unchanged' by committing all changes made since the entity was last queried
  had 'acceptChanges' called on it.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.acceptChanges();
      // The 'order' entity will now be in an 'Unchanged' state with any changes committed.
  @method acceptChanges
  **/
  proto.acceptChanges = function () {
    this._checkOperation("acceptChanges");
    var em = this.entityManager;
    if (this.entityState.isDeleted()) {
      em.detachEntity(this.entity);
    } else {
      this.setUnchanged();
    }
    em.entityChanged.publish({ entityAction: EntityAction.AcceptChanges, entity: this.entity });
  };

  /**
  Returns the entity to an EntityState of 'Unchanged' by rejecting all changes made to it since the entity was last queried
  had 'rejectChanges' called on it.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.rejectChanges();
      // The 'order' entity will now be in an 'Unchanged' state with any changes rejected.
  @method rejectChanges
  **/
  proto.rejectChanges = function () {
    this._checkOperation("rejectChanges");
    var entity = this.entity;
    var entityManager = this.entityManager;
    // we do not want PropertyChange or EntityChange events to occur here
    __using(entityManager, "isRejectingChanges", true, function () {
      rejectChangesCore(entity);
    });
    if (this.entityState.isAdded()) {
      // next line is needed because the following line will cause this.entityManager -> null;
      entityManager.detachEntity(entity);
      // need to tell em that an entity that needed to be saved no longer does.
      entityManager._notifyStateChange(entity, false);
    } else {
      if (this.entityState.isDeleted()) {
        this.entityManager._linkRelatedEntities(entity);
      }
      this.setUnchanged();
      // propertyChanged propertyName is null because more than one property may have changed.
      this.propertyChanged.publish({ entity: entity, propertyName: null });
      this.entityManager.entityChanged.publish({ entityAction: EntityAction.RejectChanges, entity: entity });
    }
  };

  function rejectChangesCore(target) {
    var aspect = target.entityAspect || target.complexAspect;
    var stype = target.entityType || target.complexType;
    var originalValues = aspect.originalValues;
    for (var propName in originalValues) {
      target.setProperty(propName, originalValues[propName]);
    }
    stype.complexProperties.forEach(function (cp) {
      var cos = target.getProperty(cp.name);
      if (cp.isScalar) {
        rejectChangesCore(cos);
      } else {
        cos._rejectChanges();
        cos.forEach(rejectChangesCore);
      }
    });
  }

  proto.getPropertyPath = function (propName) {
    return propName;
  }


  /**
  Sets the entity to an EntityState of 'Added'.  This is NOT the equivalent of calling {{#crossLink "EntityManager/addEntity"}}{{/crossLink}}
  because no key generation will occur for autogenerated keys as a result of this operation. As a result this operation can be problematic
  unless you are certain that the entity being marked 'Added' does not already exist in the database and does not have an autogenerated key.
  The same operation can be performed by calling  {{#crossLink "EntityAspect/setEntityState"}}{{/crossLink}}.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.setAdded();
      // The 'order' entity will now be in an 'Added' state.
  @method setAdded
  **/
  proto.setAdded = function () {
    return this.setEntityState(EntityState.Added);
  }

  /**
  Sets the entity to an EntityState of 'Unchanged'.  This is also the equivalent of calling {{#crossLink "EntityAspect/acceptChanges"}}{{/crossLink}}.
  The same operation can be performed by calling  {{#crossLink "EntityAspect/setEntityState"}}{{/crossLink}}.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.setUnchanged();
      // The 'order' entity will now be in an 'Unchanged' state with any changes committed.
  @method setUnchanged
  **/
  proto.setUnchanged = function () {
    return this.setEntityState(EntityState.Unchanged);
  };


  /**
  Sets the entity to an EntityState of 'Modified'.  This can also be achieved by changing the value of any property on an 'Unchanged' entity.
  The same operation can be performed by calling  {{#crossLink "EntityAspect/setEntityState"}}{{/crossLink}}.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.setModified();
      // The 'order' entity will now be in a 'Modified' state.
  @method setModified
  **/
  proto.setModified = function () {
    return this.setEntityState(EntityState.Modified);
  };

  /**
  Sets the entity to an EntityState of 'Deleted'.  This both marks the entity as being scheduled for deletion during the next 'Save' call
  but also removes the entity from all of its related entities.
  The same operation can be performed by calling  {{#crossLink "EntityAspect/setEntityState"}}{{/crossLink}}.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.setDeleted();
      // The 'order' entity will now be in a 'Deleted' state and it will no longer have any 'related' entities.
  @method setDeleted
  **/
  proto.setDeleted = function () {
    return this.setEntityState(EntityState.Deleted);
  };

  /**
  Sets the entity to an EntityState of 'Detached'.  This removes the entity from all of its related entities, but does NOT change the EntityState of any existing entities.
  The same operation can be performed by calling  {{#crossLink "EntityAspect/setEntityState"}}{{/crossLink}}.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.setDetached();
      // The 'order' entity will now be in a 'Detached' state and it will no longer have any 'related' entities.
  @method setDetached
  **/
  proto.setDetached = function () {
    return this.setEntityState(EntityState.Detached);
  }

  /**
  Sets the entity to the specified EntityState. See also 'setUnchanged', 'setModified', 'setDetached', etc.
  @example
      // assume order is an order entity attached to an EntityManager.
      order.entityAspect.setEntityState(EntityState.Unchanged);
      // The 'order' entity will now be in a 'Unchanged' state.
  @method setEntityState
  **/
  proto.setEntityState = function (entityState) {
    if (this.entityState === entityState) return false;
    this._checkOperation("setEntityState");
    if (this.entityState.isDetached()) {
      throw new Error("You cannot set the 'entityState' of an entity when it is detached - except by first attaching it to an EntityManager");
    }
    var entity = this.entity;
    var em = this.entityManager;
    var needsSave = true;
    if (entityState === EntityState.Unchanged) {
      clearOriginalValues(entity);
      delete this.hasTempKey;
      needsSave = false;
    } else if (entityState === EntityState.Added) {
      clearOriginalValues(entity);
      // TODO: more to do here... like regenerating key ???
    } else if (entityState === EntityState.Deleted) {
      if (this.entityState.isAdded()) {
        // turn it into a detach and exit early
        this.setEntityState(EntityState.Detached);
        return true;
      } else {
        // TODO: think about cascade deletes
        // entityState needs to be set it early in this one case to insure that fk's are not cleared.
        this.entityState = EntityState.Deleted;
        removeFromRelations(entity, EntityState.Deleted);
      }
    } else if (entityState === EntityState.Modified) {
      // nothing extra needed
    } else if (entityState === EntityState.Detached) {
      var group = this.entityGroup;
      // no group === already detached.
      if (!group) return false;
      group.detachEntity(entity);
      // needs to occur early here - so this IS deliberately redundent with the same code later in this method.
      this.entityState = entityState;
      removeFromRelations(entity, EntityState.Detached);
      this._detach();
      em.entityChanged.publish({ entityAction: EntityAction.Detach, entity: entity });
      needsSave = false;
    }
    this.entityState = entityState;
    em._notifyStateChange(entity, needsSave);
    return true;
  }

  function clearOriginalValues(target) {
    var aspect = target.entityAspect || target.complexAspect;
    aspect.originalValues = {};
    var stype = target.entityType || target.complexType;
    stype.complexProperties.forEach(function (cp) {
      var cos = target.getProperty(cp.name);
      if (cp.isScalar) {
        clearOriginalValues(cos);
      } else {
        cos._acceptChanges();
        cos.forEach(clearOriginalValues);
      }
    });
  }

  /**
  Performs a query for the value of a specified {{#crossLink "NavigationProperty"}}{{/crossLink}}.
  @example
      emp.entityAspect.loadNavigationProperty("Orders").then(function (data) {
          var orders = data.results;
      }).fail(function (exception) {
          // handle exception here;
      });
  @method loadNavigationProperty
  @async
  @param navigationProperty {NavigationProperty|String} The NavigationProperty or the name of the NavigationProperty to 'load'.
  @param [callback] {Function} Function to call on success.
  @param [errorCallback] {Function} Function to call on failure.
  @return {Promise}
    - properties of success promise
      - results {Array of Entity}
      - query {EntityQuery} The original query
      - httpResponse {httpResponse} The HttpResponse returned from the server.
  **/
  proto.loadNavigationProperty = function (navigationProperty, callback, errorCallback) {
    var entity = this.entity;
    var navProperty = entity.entityType._checkNavProperty(navigationProperty);
    var query = EntityQuery.fromEntityNavigation(entity, navProperty);
    // return entity.entityAspect.entityManager.executeQuery(query, callback, errorCallback);
    var promise = entity.entityAspect.entityManager.executeQuery(query);
    var that = this;
    return promise.then(function (data) {
      that._markAsLoaded(navProperty.name);
      if (callback) callback(data);
      return Q.resolve(data);
    }, function (error) {
      if (errorCallback) errorCallback(error);
      return Q.reject(error);
    });

  };

  /**
  Marks this navigationProperty on this entity as already having been loaded.
  @example
      emp.entityAspect.markNavigationPropertyAsLoaded("Orders");

  @method markAsLoaded
  @async
  @param navigationProperty {NavigationProperty|String} The NavigationProperty or name of NavigationProperty to 'load'.
  **/
  proto.markNavigationPropertyAsLoaded = function (navigationProperty) {
    var navProperty = this.entity.entityType._checkNavProperty(navigationProperty);
    this._markAsLoaded(navProperty.name);
  }

  /**
  Determines whether a navigationProperty on this entity has already been loaded.

  @example
      A navigation property is considered loaded when any of the following three conditions applies:

      1) It was fetched from the backend server.
      a) This can be the result of an expand query or a call to the EntityAspect.loadNavigationProperty method.
      b) Note that even if the fetch returns nothing the property is still marked as loaded in this case.
      2) The property is scalar and has been set to a nonnull value.
      3) The EntityAspect.markNavigationPropertyAsLoaded was called.

  @example
      var wasLoaded = emp.entityAspect.isNavigationPropertyLoaded("Orders");

  @method isNavigationPropertyLoaded
  @param navigationProperty {NavigationProperty|String} The NavigationProperty or name of NavigationProperty to 'load'.
  **/
  proto.isNavigationPropertyLoaded = function (navigationProperty) {
    var navProperty = this.entity.entityType._checkNavProperty(navigationProperty);
    if (navProperty.isScalar && this.entity.getProperty(navProperty.name) != null) {
      return true;
    }
    return this._loadedNps && this._loadedNps.indexOf(navProperty.name) >= 0;
  }

  proto._markAsLoaded = function (navPropName) {
    this._loadedNps = this._loadedNps || [];
    __arrayAddItemUnique(this._loadedNps, navPropName);
  }


  /**
  Performs validation on the entity, any errors encountered during the validation are available via the
  {{#crossLink "EntityAspect.getValidationErrors"}}{{/crossLink}} method. Validating an entity means executing
  all of the validators on both the entity itself as well as those on each of its properties.
  @example
      // assume order is an order entity attached to an EntityManager.
      var isOk = order.entityAspect.validateEntity();
      // isOk will be 'true' if there are no errors on the entity.
      if (!isOk) {
          var errors = order.entityAspect.getValidationErrors();
      }
  @method validateEntity
  @return {Boolean} Whether the entity passed validation.
  **/
  proto.validateEntity = function () {
    var ok = true;
    this._processValidationOpAndPublish(function (that) {
      ok = validateTarget(that.entity);
    });
    return ok;
  };

  // coIndex is only used where target is a complex object that is part of an array of complex objects
  // in which case ctIndex is the index of the target within the array.
  function validateTarget(target, coIndex) {
    var ok = true;
    var stype = target.entityType || target.complexType;
    var aspect = target.entityAspect || target.complexAspect;
    var entityAspect = target.entityAspect || target.complexAspect.getEntityAspect();
    var context = { entity: entityAspect.entity  };
    if (coIndex !== undefined) {
      context.index = coIndex;
    }

    stype.getProperties().forEach(function (p) {
      var value = target.getProperty(p.name);
      var validators = p.getAllValidators();
      if (validators.length > 0) {
        context.property = p;
        context.propertyName = aspect.getPropertyPath(p.name);
        ok = entityAspect._validateProperty(value, context) && ok;
      }
      if (p.isComplexProperty) {
        if (p.isScalar) {
          ok = validateTarget(value) && ok;
        } else {
          ok = value.reduce(function (pv, cv, ix) {
            return validateTarget(cv, ix) && pv;
          }, ok);
        }
      }
    });


    // then target level
    stype.getAllValidators().forEach(function (validator) {
      ok = validate(entityAspect, validator, target) && ok;
    });
    return ok;
  }


  /**
  Performs validation on a specific property of this entity, any errors encountered during the validation are available via the
  {{#crossLink "EntityAspect.getValidationErrors"}}{{/crossLink}} method. Validating a property means executing
  all of the validators on the specified property.  This call is also made automatically anytime a property
  of an entity is changed.
  @example
      // assume order is an order entity attached to an EntityManager.
      var isOk = order.entityAspect.validateProperty("Order");
  or
  @example
      var orderDateProperty = order.entityType.getProperty("OrderDate");
      var isOk = order.entityAspect.validateProperty(OrderDateProperty);
  @method validateProperty
  @param property {DataProperty|NavigationProperty|String} The {{#crossLink "DataProperty"}}{{/crossLink}} or
  {{#crossLink "NavigationProperty"}}{{/crossLink}} to validate or a string with the name of the property or a property path with
  the path to a property of a complex object.
  @param [context] {Object} A context object used to pass additional information to each  {{#crossLink "Validator"}}{{/crossLink}}
  @return {Boolean} Whether the entity passed validation.
  **/
  proto.validateProperty = function (property, context) {
    var value = this.getPropertyValue(property); // performs validations
    if (value && value.complexAspect) {
      return validateTarget(value);
    }
    context = context || {};
    context.entity = this.entity;
    if (typeof(property) === 'string') {
      context.property = this.entity.entityType.getProperty(property, true);
      context.propertyName = property;
    } else {
      context.property = property;
      context.propertyName = property.name;
    }

    return this._validateProperty(value, context);
  };

  /**
  Returns the validation errors associated with either the entire entity or any specified property.
  @example
  This method can return all of the errors for an Entity
  @example
      // assume order is an order entity attached to an EntityManager.
      var valErrors = order.entityAspect.getValidationErrors();
  as well as those for just a specific property.
  @example
      // assume order is an order entity attached to an EntityManager.
      var orderDateErrors = order.entityAspect.getValidationErrors("OrderDate");
  which can also be expressed as
  @example
      // assume order is an order entity attached to an EntityManager.
      var orderDateProperty = order.entityType.getProperty("OrderDate");
      var orderDateErrors = order.entityAspect.getValidationErrors(orderDateProperty);
  @method getValidationErrors
  @param [property] {DataProperty|NavigationProperty} The property for which validation errors should be retrieved.
  If omitted, all of the validation errors for this entity will be returned.
  @return {Array of ValidationError}
  **/
  proto.getValidationErrors = function (property) {
    assertParam(property, "property").isOptional().isEntityProperty().or().isString().check();
    var result = __getOwnPropertyValues(this._validationErrors);
    if (property) {
      var propertyName = typeof (property) === 'string' ? property : property.name;
      result = result.filter(function (ve) {
        return ve.property && (ve.property.name === propertyName || (propertyName.indexOf(".") != -1 && ve.propertyName == propertyName));
      });
    }
    return result;
  };

  /**
  Adds a validation error.
  @method addValidationError
  @param validationError {ValidationError}
  **/
  proto.addValidationError = function (validationError) {
    assertParam(validationError, "validationError").isInstanceOf(ValidationError).check();
    this._processValidationOpAndPublish(function (that) {
      that._addValidationError(validationError);
    });
  };

  /**
  Removes a validation error.
  @method removeValidationError
  @param validationErrorOrKey {ValidationError|String} Either a ValidationError or a ValidationError 'key' value
  **/
  proto.removeValidationError = function (validationErrorOrKey) {
    assertParam(validationErrorOrKey, "validationErrorOrKey").isString().or().isInstanceOf(ValidationError).or().isInstanceOf(Validator).check();

    var key = (typeof (validationErrorOrKey) === "string") ? validationErrorOrKey : validationErrorOrKey.key;
    this._processValidationOpAndPublish(function (that) {
      that._removeValidationError(key);
    });
  };

  /**
  Removes all of the validation errors for a specified entity
  @method clearValidationErrors
  **/
  proto.clearValidationErrors = function () {
    this._processValidationOpAndPublish(function (that) {
      __objectForEach(that._validationErrors, function (key, valError) {
        if (valError) {
          delete that._validationErrors[key];
          that._pendingValidationResult.removed.push(valError);
        }
      });
      that.hasValidationErrors = !__isEmpty(that._validationErrors);
    });
  };


  // returns null for np's that do not have a parentKey
  proto.getParentKey = function (navigationProperty) {
    // NavigationProperty doesn't yet exist
    // assertParam(navigationProperty, "navigationProperty").isInstanceOf(NavigationProperty).check();
    var fkNames = navigationProperty.foreignKeyNames;
    if (fkNames.length === 0) return null;
    var that = this;
    var fkValues = fkNames.map(function (fkn) {
      return that.entity.getProperty(fkn);
    });
    return new EntityKey(navigationProperty.entityType, fkValues);
  };

  proto.getPropertyValue = function (property) {
    assertParam(property, "property").isString().or().isEntityProperty().check();
    var value;
    if (typeof (property) === 'string') {
      var propNames = property.trim().split(".");
      var propName = propNames.shift();
      value = this.entity;
      value = value.getProperty(propName);
      while (propNames.length > 0) {
        propName = propNames.shift();
        value = value.getProperty(propName);
      }
    } else {
      if (!(property.parentType instanceof EntityType)) {
        throw new Error("The validateProperty method does not accept a 'property' parameter whose parentType is a ComplexType; " +
            "Pass a 'property path' string as the 'property' parameter instead ");
      }
      value = this.entity.getProperty(property.name);
    }
    return value;
  };

  // internal methods

  proto._checkOperation = function(operationName) {
    if (this.isBeingSaved) {
      throw new Error("Cannot perform a '" + operationName + "' on an entity that is in the process of being saved");
    }
    // allows chaining
    return this;
  }

  proto._detach = function () {
    this.entityGroup = null;
    this.entityManager = null;
    this.entityState = EntityState.Detached;
    this.originalValues = {};
    this._validationErrors = {};
    this.hasValidationErrors = false;
    this.validationErrorsChanged.clear();
    this.propertyChanged.clear();
  };


  // called from defaultInterceptor.
  proto._validateProperty = function (value, context) {
    var ok = true;
    this._processValidationOpAndPublish(function (that) {
      context.property.getAllValidators().forEach(function (validator) {
        ok = validate(that, validator, value, context) && ok;
      });
    });
    return ok;
  };

  proto._processValidationOpAndPublish = function (validationFn) {
    if (this._pendingValidationResult) {
      // only top level processValidations call publishes
      validationFn(this);
    } else {
      try {
        this._pendingValidationResult = { entity: this.entity, added: [], removed: [] };
        validationFn(this);
        if (this._pendingValidationResult.added.length > 0 || this._pendingValidationResult.removed.length > 0) {
          this.validationErrorsChanged.publish(this._pendingValidationResult);
          // this might be a detached entity hence the guard below.
          this.entityManager && this.entityManager.validationErrorsChanged.publish(this._pendingValidationResult);

        }
      } finally {
        this._pendingValidationResult = undefined;
      }
    }
  };

  proto._addValidationError = function (validationError) {
    this._validationErrors[validationError.key] = validationError;
    this.hasValidationErrors = true;
    this._pendingValidationResult.added.push(validationError);
  };

  proto._removeValidationError = function (key) {
    var valError = this._validationErrors[key];
    if (valError) {
      delete this._validationErrors[key];
      this.hasValidationErrors = !__isEmpty(this._validationErrors);
      this._pendingValidationResult.removed.push(valError);
    }
  };

  function removeFromRelations(entity, entityState) {
    // remove this entity from any collections.
    // mark the entity deleted or detached

    var isDeleted = entityState.isDeleted();
    if (isDeleted) {
      removeFromRelationsCore(entity);
    } else {
      __using(entity.entityAspect.entityManager, "isLoading", true, function () {
        removeFromRelationsCore(entity);
      });
    }
  }

  function removeFromRelationsCore(entity) {
    entity.entityType.navigationProperties.forEach(function (np) {
      var inverseNp = np.getInverse();
      var npValue = entity.getProperty(np.name);
      if (np.isScalar) {
        if (npValue) {
          if (inverseNp) {
            if (inverseNp.isScalar) {
              npValue.setProperty(inverseNp.name, null);
            } else {
              var collection = npValue.getProperty(inverseNp.name);
              if (collection.length) {
                __arrayRemoveItem(collection, entity);
              }
            }
          }
          entity.setProperty(np.name, null);
        }
      } else {
        if (inverseNp) {
          // npValue is a live list so we need to copy it first.
          npValue.slice(0).forEach(function (v) {
            if (inverseNp.isScalar) {
              v.setProperty(inverseNp.name, null);
            } else {
              // TODO: many to many - not yet handled.
            }
          });
        }
        // now clear it.
        npValue.length = 0;
      }
    });

  };

  // note entityAspect only - ( no complex aspect allowed on the call).
  function validate(entityAspect, validator, value, context) {
    var ve = validator.validate(value, context);
    if (ve) {
      entityAspect._addValidationError(ve);
      return false;
    } else {
      var key = ValidationError.getKey(validator, context ? context.propertyName : null);
      entityAspect._removeValidationError(key);
      return true;
    }
  }



  return ctor;

})();

var ComplexAspect = (function () {

  /**
  An ComplexAspect instance is associated with every complex object instance and is accessed via the complex object's 'complexAspect' property.

  The ComplexAspect itself provides properties to determine the parent object, parent property and original values for the complex object.

  A ComplexAspect will almost never need to be constructed directly. You will usually get an ComplexAspect by accessing
  an entities 'complexAspect' property.  This property will be automatically attached when an complex object is created as part of an
  entity via either a query, import or EntityManager.createEntity call.
  @example
      // assume address is a complex property on the 'Customer' type
      var aspect = aCustomer.address.complexAspect;
      // aCustomer === aspect.parent;
  @class ComplexAspect
  **/
  var ctor = function ComplexAspect(complexObject, parent, parentProperty) {
    if (!complexObject) {
      throw new Error("The  ComplexAspect ctor requires an entity as its only argument.");
    }
    if (complexObject.complexAspect) {
      return complexObject.complexAspect;
    }
    // if called without new
    if (!(this instanceof ComplexAspect)) {
      return new ComplexAspect(complexObject, parent, parentProperty);
    }

    // entityType should already be on the entity from 'watch'
    this.complexObject = complexObject;
    complexObject.complexAspect = this;

    // TODO: keep public or not?
    this.originalValues = {};

    // if a standalone complexObject
    if (parent != null) {
      this.parent = parent;
      this.parentProperty = parentProperty;
    }

    var complexType = complexObject.complexType;
    if (!complexType) {
      var typeName = complexObject.prototype._$typeName;
      if (!typeName) {
        throw new Error("This entity is not registered as a valid ComplexType");
      } else {
        throw new Error("Metadata for this complexType has not yet been resolved: " + typeName);
      }
    }
    var complexCtor = complexType.getCtor();
    __modelLibraryDef.getDefaultInstance().startTracking(complexObject, complexCtor.prototype);

  };
  var proto = ctor.prototype;


  /**
  The complex object that this aspect is associated with.

  __readOnly__
  @property complexObject {Entity}
  **/

  /**
  The parent object that to which this aspect belongs; this will either be an entity or another complex object.

  __readOnly__
  @property parent {Entity|ComplexObject}
  **/

  /**
  The {{#crossLink "DataProperty"}}{{/crossLink}} on the 'parent' that contains this complex object.

  __readOnly__
  @property parentProperty {DataProperty}
  **/

  /**
  The 'original values' of this complex object where they are different from the 'current values'.
  This is a map where the key is a property name and the value is the 'original value' of the property.

  __readOnly__
  @property originalValues {Object}
  **/

  /**
  Returns the EntityAspect for the top level entity tht contains this complex object.

  @method getEntityAspect
  @return  {String}
  **/
  proto.getEntityAspect = function () {
    var parent = this.parent;
    if (!parent) return new EntityAspect(null);
    var entityAspect = parent.entityAspect;
    while (parent && !entityAspect) {
      parent = parent.complexAspect && parent.complexAspect.parent;
      entityAspect = parent && parent.entityAspect;
    }
    return entityAspect || new EntityAspect(null);
  }

  /**
  Executes the specified query against this EntityManager's local cache.

  @method getPropertyPath
  @param propName {String}  The property name of a property on this complex aspect for which we want the full path.
  @return  {String}    The 'property path' from the top level entity that contains this complex object to this object.
  **/
  proto.getPropertyPath = function (propName) {
    var parent = this.parent;
    if (!parent) return null;
    var aspect = parent.complexAspect || parent.entityAspect;
    return aspect.getPropertyPath(this.parentProperty.name + "." + propName);
  }

  return ctor;

})();


breeze.EntityAspect = EntityAspect;
breeze.ComplexAspect = ComplexAspect;
;/**
@module breeze
**/

var EntityKey = (function () {

  var ENTITY_KEY_DELIMITER = ":::";

  /**
  An EntityKey is an object that represents the unique identity of an entity.  EntityKey's are immutable.

  @class EntityKey
  **/

  /**
  Constructs a new EntityKey.  Each entity within an EntityManager will have a unique EntityKey.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var empType = em1.metadataStore.getEntityType("Employee");
      var entityKey = new EntityKey(empType, 1);
  EntityKey's may also be found by calling EntityAspect.getKey()
  @example
      // assume employee1 is an existing Employee entity
      var empKey = employee1.entityAspect.getKey();
  Multipart keys are created by passing an array as the 'keyValues' parameter
  @example
      var empTerrType = em1.metadataStore.getEntityType("EmployeeTerritory");
      var empTerrKey = new EntityKey(empTerrType, [ 1, 77]);
      // The order of the properties in the 'keyValues' array must be the same as that
      // returned by empTerrType.keyProperties
  @method <ctor> EntityKey
  @param entityType {EntityType} The {{#crossLink "EntityType"}}{{/crossLink}} of the entity.
  @param keyValues {value|Array of values} A single value or an array of values.
  **/
  var ctor = function EntityKey(entityType, keyValues) {
    assertParam(entityType, "entityType").isInstanceOf(EntityType).check();
    var subtypes = entityType.getSelfAndSubtypes();
    if (subtypes.length > 1) {
      this._subtypes = subtypes.filter(function (st) {
        return st.isAbstract === false;
      });
    }

    if (!Array.isArray(keyValues)) {
      keyValues = __arraySlice(arguments, 1);
    }

    this.entityType = entityType;
    entityType.keyProperties.forEach(function (kp, i) {
      // insure that guid keys are comparable.
      if (kp.dataType === DataType.Guid) {
        keyValues[i] = keyValues[i] && keyValues[i].toLowerCase();
      }
    });

    this.values = keyValues;
    this._keyInGroup = createKeyString(keyValues);

  };

  ctor._$typeName = "EntityKey";
  var proto = ctor.prototype;

  /**
  The 'EntityType' that this is a key for.

  __readOnly__
  @property entityType {EntityType}
  **/

  /**
  An array of the values for this key. This will usually only have a single element, unless the entity type has a multipart key.

  __readOnly__
  @property values {Array}
  **/

  proto.toJSON = function () {
    return {
      entityType: this.entityType.name,
      values: this.values
    };
  };

  ctor.fromJSON = function (json, metadataStore) {
    var et = metadataStore._getEntityType(json.entityType, true);
    return new EntityKey(et, json.values);
  };

  /**
  Used to compare EntityKeys are determine if they refer to the same Entity.
  There is also an static version of 'equals' with the same functionality.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var empType = em1.metadataStore.getEntityType("Employee");
      var empKey1 = new EntityKey(empType, 1);
      // assume employee1 is an existing Employee entity
      var empKey2 = employee1.entityAspect.getKey();
      if (empKey1.equals(empKey2)) {
          // do something  ...
      }
  @method equals
  @param entityKey {EntityKey}
  **/
  proto.equals = function (entityKey) {
    if (!(entityKey instanceof EntityKey)) return false;
    return (this.entityType === entityKey.entityType) &&
        __arrayEquals(this.values, entityKey.values);
  };

  /*
  Returns a human readable representation of this EntityKey.
  @method toString
  */
  proto.toString = function (altEntityType) {
    return (altEntityType || this.entityType).name + '-' + this._keyInGroup;
  };

  /**
  Used to compare EntityKeys are determine if they refer to the same Entity.
  There is also an instance version of 'equals' with the same functionality.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var empType = em1.metadataStore.getEntityType("Employee");
      var empKey1 = new EntityKey(empType, 1);
      // assume employee1 is an existing Employee entity
      var empKey2 = employee1.entityAspect.getKey();
      if (EntityKey.equals(empKey1, empKey2)) {
          // do something  ...
      }
  @method equals
  @static
  @param k1 {EntityKey}
  @param k2 {EntityKey}
  **/
  ctor.equals = function (k1, k2) {
    if (!(k1 instanceof EntityKey)) return false;
    return k1.equals(k2);
  };

  // TODO: we may want to compare to default values later.
  proto._isEmpty = function () {
    return this.values.join("").length === 0;
  };

  ctor.createKeyString = createKeyString;

  function createKeyString(keyValues) {
    return keyValues.join(ENTITY_KEY_DELIMITER);
  }

  return ctor;
})();

breeze.EntityKey = EntityKey;
;/**
@module breeze
**/

var EntityState = (function () {
  /**
  EntityState is an 'Enum' containing all of the valid states for an 'Entity'.

  @class EntityState
  @static
  **/
  var entityStateMethods = {
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isUnchanged();
    is the same as
    @example
        return es === EntityState.Unchanged;
    @method isUnchanged
    @return {Boolean} Whether an entityState instance is EntityState.Unchanged.
    **/
    isUnchanged: function () {
      return this === EntityState.Unchanged;
    },
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isAdded();
    is the same as
    @example
        return es === EntityState.Added;
    @method isAdded
    @return {Boolean} Whether an entityState instance is EntityState.Added.
    **/
    isAdded: function () {
      return this === EntityState.Added;
    },
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isModified();
    is the same as
    @example
        return es === EntityState.Modified;
    @method isModified
    @return {Boolean} Whether an entityState instance is EntityState.Modified.
    **/
    isModified: function () {
      return this === EntityState.Modified;
    },
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isDeleted();
    is the same as
    @example
        return es === EntityState.Deleted;
    @method isDeleted
    @return  {Boolean} Whether an entityState instance is EntityState.Deleted.
    **/
    isDeleted: function () {
      return this === EntityState.Deleted;
    },
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isDetached();
    is the same as
    @example
        return es === EntityState.Detached;
    @method isDetached
    @return  {Boolean} Whether an entityState instance is EntityState.Detached.
    **/
    isDetached: function () {
      return this === EntityState.Detached;
    },
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isUnchangedOrModified();
    is the same as
    @example
        return es === EntityState.Unchanged || es === EntityState.Modified
    @method isUnchangedOrModified
    @return {Boolean} Whether an entityState instance is EntityState.Unchanged or EntityState.Modified.
    **/
    isUnchangedOrModified: function () {
      return this === EntityState.Unchanged || this === EntityState.Modified;
    },
    /**
    @example
        var es = anEntity.entityAspect.entityState;
        return es.isAddedModifiedOrDeleted();
    is the same as
    @example
        return es === EntityState.Added || es === EntityState.Modified || es === EntityState.Deleted
    @method isAddedModifiedOrDeleted
    @return {Boolean} Whether an entityState instance is EntityState.Added or EntityState.Modified or EntityState.Deleted.
    **/
    isAddedModifiedOrDeleted: function () {
      return this === EntityState.Added ||
          this === EntityState.Modified ||
          this === EntityState.Deleted;
    }
  };

  var EntityState = new Enum("EntityState", entityStateMethods);
  /**
  The 'Unchanged' state.

  @property Unchanged {EntityState}
  @final
  @static
  **/
  EntityState.Unchanged = EntityState.addSymbol();
  /**
  The 'Added' state.

  @property Added {EntityState}
  @final
  @static
  **/
  EntityState.Added = EntityState.addSymbol();
  /**
  The 'Modified' state.

  @property Modified {EntityState}
  @final
  @static
  **/
  EntityState.Modified = EntityState.addSymbol();
  /**
  The 'Deleted' state.

  @property Deleted {EntityState}
  @final
  @static
  **/
  EntityState.Deleted = EntityState.addSymbol();
  /**
  The 'Detached' state.

  @property Detached {EntityState}
  @final
  @static
  **/
  EntityState.Detached = EntityState.addSymbol();
  EntityState.resolveSymbols();
  return EntityState;
})();

breeze.EntityState = EntityState;
;breeze.makePrimitiveArray = (function () {
  var primitiveArrayMixin = {};

  // complexArray will have the following props
  //    parent
  //    propertyPath
  //    parentProperty
  //    addedItems  - only if modified
  //    removedItems  - only if modified
  //  each complexAspect of any entity within a complexArray
  //  will have its own _complexState = "A/M";

  /**
  Primitive arrays are not actually classes, they are objects that mimic arrays. A primitive array is collection of
  primitive types associated with a data property on a single entity or complex object. i.e. customer.invoiceNumbers.
  This collection looks like an array in that the basic methods on arrays such as 'push', 'pop', 'shift', 'unshift', 'splice'
  are all provided as well as several special purpose methods.
  @class {primitiveArray}
  **/

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever the contents of this array changed.  This event
  is fired any time a new entity is attached or added to the EntityManager and happens to belong to this collection.
  Adds that occur as a result of query or import operations are batched so that all of the adds or removes to any individual
  collections are collected into a single notification event for each relation array.
  @example
      // assume order is an order entity attached to an EntityManager.
      orders.arrayChanged.subscribe(
      function (arrayChangedArgs) {
          var addedEntities = arrayChangedArgs.added;
          var removedEntities = arrayChanged.removed;
      });
  @event arrayChanged
  @param added {Array of Primitives} An array of all of the items added to this collection.
  @param removed {Array of Primitives} An array of all of the items removed from this collection.
  @readOnly
  **/

    // virtual impls
  primitiveArrayMixin._getGoodAdds = function (adds) {
    return adds;
  };

  primitiveArrayMixin._beforeChange = function () {
    var entityAspect = this.getEntityAspect();
    if (entityAspect.entityState.isUnchanged()) {
      entityAspect.setModified();
    }
    if (entityAspect.entityState.isModified() && !this._origValues) {
      this._origValues = this.slice(0);
    }
  };

  primitiveArrayMixin._processAdds = function (adds) {
    // nothing needed
  };

  primitiveArrayMixin._processRemoves = function (removes) {
    // nothing needed;
  };
  //

  primitiveArrayMixin._rejectChanges = function () {
    if (!this._origValues) return;
    this.length = 0;
    Array.prototype.push.apply(this, this._origValues);
  };

  primitiveArrayMixin._acceptChanges = function () {
    this._origValues = null;
  };

  // local functions

  function makePrimitiveArray(arr, parent, parentProperty) {

    observableArray.initializeParent(arr, parent, parentProperty);
    arr.arrayChanged = new Event("arrayChanged", arr);
    __extend(arr, observableArray.mixin);
    return __extend(arr, primitiveArrayMixin);
  }

  return makePrimitiveArray;
})();;breeze.makeRelationArray = (function () {

  var relationArrayMixin = {};

  /**
  Relation arrays are not actually classes, they are objects that mimic arrays. A relation array is collection of
  entities associated with a navigation property on a single entity. i.e. customer.orders or order.orderDetails.
  This collection looks like an array in that the basic methods on arrays such as 'push', 'pop', 'shift', 'unshift', 'splice'
  are all provided as well as several special purpose methods.
  @class {relationArray}
  **/

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever the contents of this array changed.  This event
  is fired any time a new entity is attached or added to the EntityManager and happens to belong to this collection.
  Adds that occur as a result of query or import operations are batched so that all of the adds or removes to any individual
  collections are collected into a single notification event for each relation array.
  @example
      // assume order is an order entity attached to an EntityManager.
      orders.arrayChanged.subscribe(
      function (arrayChangedArgs) {
          var addedEntities = arrayChangedArgs.added;
          var removedEntities = arrayChanged.removed;
      });
  @event arrayChanged
  @param added {Array of Entity} An array of all of the entities added to this collection.
  @param removed {Array of Entity} An array of all of the removed from this collection.
  @readOnly
  **/


  /**
  Performs an asynchronous load of all other the entities associated with this relationArray.
  @example
      // assume orders is an empty, as yet unpopulated, relation array of orders
      // associated with a specific customer.
      orders.load().then(...)
  @method load
  @param [callback] {Function}
  @param [errorCallback] {Function}
  @return {Promise}
  **/
  relationArrayMixin.load = function (callback, errorCallback) {
    var parent = this.parentEntity;
    var query = EntityQuery.fromEntityNavigation(this.parentEntity, this.navigationProperty);
    var em = parent.entityAspect.entityManager;
    return em.executeQuery(query, callback, errorCallback);
  };

  relationArrayMixin._getEventParent = function () {
    return this.parentEntity.entityAspect;
  };

  relationArrayMixin._getPendingPubs = function () {
    var em = this.parentEntity.entityAspect.entityManager;
    return em && em._pendingPubs;
  };

  // virtual impls
  relationArrayMixin._getGoodAdds = function (adds) {
    return getGoodAdds(this, adds);
  };

  relationArrayMixin._processAdds = function (adds) {
    processAdds(this, adds);
  };

  relationArrayMixin._processRemoves = function (removes) {
    processRemoves(this, removes);
  };
  //

  function getGoodAdds(relationArray, adds) {
    var goodAdds = checkForDups(relationArray, adds);
    if (!goodAdds.length) {
      return goodAdds;
    }
    var parentEntity = relationArray.parentEntity;
    var entityManager = parentEntity.entityAspect.entityManager;
    // we do not want to attach an entity during loading
    // because these will all be 'attached' at a later step.
    if (entityManager && !entityManager.isLoading) {
      goodAdds.forEach(function (add) {
        if (add.entityAspect.entityState.isDetached()) {
          relationArray._inProgress = true;
          try {
            entityManager.attachEntity(add, EntityState.Added);
          } finally {
            relationArray._inProgress = false;
          }
        }
      });
    }
    return goodAdds;
  }

  function processAdds(relationArray, adds) {
    var parentEntity = relationArray.parentEntity;
    var np = relationArray.navigationProperty;
    var addsInProcess = relationArray._addsInProcess;

    var invNp = np.getInverse();
    var startIx = addsInProcess.length;
    try {
      adds.forEach(function (childEntity) {
        addsInProcess.push(childEntity);
        if (invNp) {
          childEntity.setProperty(invNp.name, parentEntity);
        } else {
          // This occurs with a unidirectional 1-n navigation - in this case
          // we need to update the fks instead of the navProp
          var pks = parentEntity.entityType.keyProperties;
          np.invForeignKeyNames.forEach(function (fk, i) {
            childEntity.setProperty(fk, parentEntity.getProperty(pks[i].name));
          });
        }
      });
    } finally {
      addsInProcess.splice(startIx, adds.length);
    }

  }

  function processRemoves(relationArray, removes) {
    var inp = relationArray.navigationProperty.getInverse();
    if (inp) {
      removes.forEach(function (childEntity) {
        childEntity.setProperty(inp.name, null);
      });
    }
  }

  function checkForDups(relationArray, adds) {
    // don't allow dups in this array. - also prevents recursion
    var parentEntity = relationArray.parentEntity;
    var navProp = relationArray.navigationProperty;
    var inverseProp = navProp.getInverse();
    var goodAdds;
    if (inverseProp) {
      goodAdds = adds.filter(function (a) {
        if (relationArray._addsInProcess.indexOf(a) >= 0) {
          return false;
        }
        var inverseValue = a.getProperty(inverseProp.name);
        return inverseValue !== parentEntity;
      });
    } else {
      // This occurs with a unidirectional 1->N relation ( where there is no n -> 1)
      // in this case we compare fks.
      var fkPropNames = navProp.invForeignKeyNames;
      if (navProp.baseProperty && (!fkPropNames.length)) fkPropNames = navProp.baseProperty.invForeignKeyNames; // TODO climb hierarchy
      var keyProps = parentEntity.entityType.keyProperties;
      goodAdds = adds.filter(function (a) {
        if (relationArray._addsInProcess.indexOf(a) >= 0) {
          return false;
        }
        return fkPropNames.some(function (fk, i) {
          var keyProp = keyProps[i].name;
          var keyVal = parentEntity.getProperty(keyProp);
          var fkVal = a.getProperty(fk);
          return keyVal !== fkVal;
        });
      });
    }
    return goodAdds;
  }

  function makeRelationArray(arr, parentEntity, navigationProperty) {
    arr.parentEntity = parentEntity;
    arr.navigationProperty = navigationProperty;
    arr.arrayChanged = new Event("arrayChanged", arr);
    // array of pushes currently in process on this relation array - used to prevent recursion.
    arr._addsInProcess = [];
    // need to use mixins here instead of inheritance because we are starting from an existing array object.
    __extend(arr, observableArray.mixin);
    return __extend(arr, relationArrayMixin);
  }

  return makeRelationArray;
})();;function defaultPropertyInterceptor(property, newValue, rawAccessorFn) {
  // 'this' is the entity itself in this context.

  if (newValue === undefined) newValue = null;
  var oldValue = rawAccessorFn();

  var dataType = property.dataType;
  if (dataType && dataType.parse) {
    // attempts to coerce a value to the correct type - if this fails return the value unchanged
    if (Array.isArray(newValue) && !property.isScalar) {
      newValue = newValue.map(function (nv) {
        return dataType.parse(nv, typeof nv);
      });
    } else {
      newValue = dataType.parse(newValue, typeof newValue);
    }
  }

  // exit if no change - extra cruft is because dateTimes don't compare cleanly.
  if (newValue === oldValue || (dataType && dataType.normalize && newValue && oldValue && dataType.normalize(newValue) === dataType.normalize(oldValue))) {
    return;
  }

  // CANNOT DO NEXT LINE because it has the possibility of creating a new property
  // 'entityAspect' on 'this'.  - Not permitted by IE inside of a defined property on a prototype.
  // var entityAspect = new EntityAspect(this);

  var propertyName;
  var entityAspect = this.entityAspect;
  if (entityAspect) {
    propertyName = property.name;
  } else {
    var localAspect = this.complexAspect;
    if (localAspect) {
      entityAspect = localAspect.getEntityAspect();
      propertyName = localAspect.getPropertyPath(property.name);
    } else {
      // does not yet have an EntityAspect so just set the prop
      rawAccessorFn(newValue);
      return;
    }
  }

  // Note that we need to handle multiple properties in process, not just one in order to avoid recursion.
  // ( except in the case of null propagation with fks where null -> 0 in some cases.)
  // (this may not be needed because of the newValue === oldValue test above)
  var inProcess = entityAspect._inProcess = entityAspect._inProcess || [];
  // check for recursion
  if (inProcess.indexOf(property) >= 0) return;
  inProcess.push(property);

  try {

    var context = {
      parent: this,
      property: property,
      newValue: newValue,
      oldValue: oldValue,
      propertyName: propertyName,
      entityAspect: entityAspect
    }

    if (property.isComplexProperty) {
      setDpValueComplex(context, rawAccessorFn);
    } else if (property.isDataProperty) {
      setDpValueSimple(context, rawAccessorFn);
    } else {
      setNpValue(context, rawAccessorFn);
    }

    postChangeEvents(context);

  } finally {
    inProcess.pop();
  }
}

function setDpValueSimple(context, rawAccessorFn) {
  var parent = context.parent;
  var property = context.property;
  var entityAspect = context.entityAspect;
  var oldValue = context.oldValue;
  var newValue = context.newValue;

  var entityManager = entityAspect.entityManager;
  // 'entityType' on the next line be null for complex properties but it will only be ref'd within this
  // fn when the property is part of the key
  var entityType = parent.entityType;

  if (!property.isScalar) {
    throw new Error("Nonscalar data properties are readonly - items may be added or removed but the collection may not be changed.");
  }

  // store an original value for this property if not already set
  if (entityAspect.entityState.isUnchangedOrModified()) {
    var propName = property.name;
    // localAspect is not the same as entityAspect for complex props
    var localAspect = parent.entityAspect || parent.complexAspect;
    if (localAspect.originalValues[propName] === undefined) {
      // otherwise this entry will be skipped during serialization
      localAspect.originalValues[propName] = oldValue !== undefined ? oldValue : property.defaultValue;
    }
  }

  // if we are changing the key update our internal entityGroup indexes.
  if (property.isPartOfKey && entityManager && !entityManager.isLoading) {
    var keyProps = entityType.keyProperties;
    var values = keyProps.map(function (p) {
      if (p === property) {
        return newValue;
      } else {
        return parent.getProperty(p.name);
      }
    });
    var newKey = new EntityKey(entityType, values);
    if (entityManager.findEntityByKey(newKey)) {
      throw new Error("An entity with this key is already in the cache: " + newKey.toString());
    }
    var oldKey = parent.entityAspect.getKey();
    var eg = entityManager._findEntityGroup(entityType);
    eg._replaceKey(oldKey, newKey);
  }

  // process related updates ( the inverse relationship) first so that collection dups check works properly.
  // update inverse relationship

  var relatedNavProp = property.relatedNavigationProperty;
  if (relatedNavProp && entityManager) {
    // Example: bidirectional fkDataProperty: 1->n: order -> orderDetails
    // orderDetail.orderId <- newOrderId || null
    //    ==> orderDetail.order = lookupOrder(newOrderId)
    //    ==> (see set navProp above)
    //       and
    // Example: bidirectional fkDataProperty: 1->1: order -> internationalOrder
    // internationalOrder.orderId <- newOrderId || null
    //    ==> internationalOrder.order = lookupOrder(newOrderId)
    //    ==> (see set navProp above)

    if (newValue != null) {
      var key = new EntityKey(relatedNavProp.entityType, [newValue]);
      var relatedEntity = entityManager.findEntityByKey(key);

      if (relatedEntity) {
        parent.setProperty(relatedNavProp.name, relatedEntity);
      } else {
        // it may not have been fetched yet in which case we want to add it as an unattachedChild.
        entityManager._unattachedChildrenMap.addChild(key, relatedNavProp, parent);
        parent.setProperty(relatedNavProp.name, null);
      }
    } else {
      parent.setProperty(relatedNavProp.name, null);
    }
  } else if (property.inverseNavigationProperty && entityManager && !entityManager._inKeyFixup) {
    // Example: unidirectional fkDataProperty: 1->n: region -> territories
    // territory.regionId <- newRegionId
    //    ==> lookupRegion(newRegionId).territories.push(territory)
    //                and
    // Example: unidirectional fkDataProperty: 1->1: order -> internationalOrder
    // internationalOrder.orderId <- newOrderId
    //    ==> lookupOrder(newOrderId).internationalOrder = internationalOrder
    //                and
    // Example: unidirectional fkDataProperty: 1->n: region -> territories
    // territory.regionId <- null
    //    ==> lookupRegion(territory.oldRegionId).territories.remove(oldTerritory);
    //                and
    // Example: unidirectional fkDataProperty: 1->1: order -> internationalOrder
    // internationalOrder.orderId <- null
    //    ==> lookupOrder(internationalOrder.oldOrderId).internationalOrder = null;

    var invNavProp = property.inverseNavigationProperty;

    if (oldValue != null) {
      key = new EntityKey(invNavProp.parentType, [oldValue]);
      relatedEntity = entityManager.findEntityByKey(key);
      if (relatedEntity) {
        if (invNavProp.isScalar) {
          relatedEntity.setProperty(invNavProp.name, null);
        } else {
          // remove 'this' from old related nav prop
          var relatedArray = relatedEntity.getProperty(invNavProp.name);
          // arr.splice(arr.indexOf(value_to_remove), 1);
          relatedArray.splice(relatedArray.indexOf(parent), 1);
        }
      }
    }

    if (newValue != null) {
      key = new EntityKey(invNavProp.parentType, [newValue]);
      relatedEntity = entityManager.findEntityByKey(key);

      if (relatedEntity) {
        if (invNavProp.isScalar) {
          relatedEntity.setProperty(invNavProp.name, parent);
        } else {
          relatedEntity.getProperty(invNavProp.name).push(parent);
        }
      } else {
        // it may not have been fetched yet in which case we want to add it as an unattachedChild.
        entityManager._unattachedChildrenMap.addChild(key, invNavProp, parent);
      }
    }

  }

  rawAccessorFn(newValue);
  updateStateAndValidate(context);

// Changes End

  // if (property.isPartOfKey && (!this.complexAspect)) {
  if (property.isPartOfKey) {
    // propogate pk change to all related entities;

    var propertyIx = entityType.keyProperties.indexOf(property);
    // this part handles order.orderId => orderDetail.orderId
    // but won't handle product.productId => orderDetail.productId because product
    // doesn't have an orderDetails property.
    entityType.navigationProperties.forEach(function (np) {
      var inverseNp = np.getInverse();
      var fkNames = inverseNp ? inverseNp.foreignKeyNames : np.invForeignKeyNames;

      if (fkNames.length === 0) return;
      var npValue = parent.getProperty(np.name);
      if (!npValue) return;
      var fkName = fkNames[propertyIx];
      if (np.isScalar) {
        npValue.setProperty(fkName, newValue);
      } else {
        npValue.forEach(function (iv) {
          iv.setProperty(fkName, newValue);
        });
      }
    });
    // this handles unidirectional problems not covered above.
    if (entityManager) {
      entityType.inverseForeignKeyProperties.forEach(function (invFkProp) {
        if (invFkProp.relatedNavigationProperty.inverse == null) {
          // this next step may be slow - it iterates over all of the entities in a group;
          // hopefully it doesn't happen often.
          entityManager._updateFkVal(invFkProp, oldValue, newValue);
        }
        ;
      });
    }

    // insure that cached key is updated.
    entityAspect.getKey(true);
  }
}

function setDpValueComplex(context, rawAccessorFn) {
  var property = context.property;
  var oldValue = context.oldValue;
  var newValue = context.newValue;

  var dataType = property.dataType;
  if (property.isScalar) {
    if (!newValue) {
      throw new Error(__formatString("You cannot set the '%1' property to null because it's datatype is the ComplexType: '%2'", property.name, property.dataType.name));
    }
    // To get here it must be a ComplexProperty
    // 'dataType' will be a complexType
    if (!oldValue) {
      var ctor = dataType.getCtor();
      oldValue = new ctor();
      rawAccessorFn(oldValue);
    }
    dataType.dataProperties.forEach(function (dp) {
      var pn = dp.name;
      var nv = newValue.getProperty(pn);
      oldValue.setProperty(pn, nv);
    });
  } else {
    throw new Error(__formatString("You cannot set the non-scalar complex property: '%1' on the type: '%2'." +
            "Instead get the property and use array functions like 'push' or 'splice' to change its contents.",
        property.name, property.parentType.name));
  }
}

function setNpValue(context, rawAccessorFn) {

  var parent = context.parent;
  var property = context.property;
  var entityAspect = context.entityAspect;
  var oldValue = context.oldValue;
  var newValue = context.newValue;

  if (!property.isScalar) {
    throw new Error("Nonscalar navigation properties are readonly - entities can be added or removed but the collection may not be changed.");
  }

  var entityManager = entityAspect.entityManager;
  var inverseProp = property.getInverse();

  // manage attachment -
  if (newValue != null) {
    var newAspect = newValue.entityAspect;
    if (entityManager) {
      if (newAspect.entityState.isDetached()) {
        if (!entityManager.isLoading) {
          entityManager.attachEntity(newValue, EntityState.Added);
        }
      } else {
        if (newAspect.entityManager !== entityManager) {
          throw new Error("An Entity cannot be attached to an entity in another EntityManager. One of the two entities must be detached first.");
        }
      }
    } else {
      if (newAspect && newAspect.entityManager) {
        entityManager = newAspect.entityManager;
        if (!entityManager.isLoading) {
          entityManager.attachEntity(entityAspect.entity, EntityState.Added);
        }
      }
    }
  }

  // process related updates ( the inverse relationship) first so that collection dups check works properly.
  // update inverse relationship
  if (inverseProp) {
    ///
    if (inverseProp.isScalar) {
      // Example: bidirectional navProperty: 1->1: order -> internationalOrder
      // order.internationalOrder <- internationalOrder || null
      //    ==> (oldInternationalOrder.order = null)
      //    ==> internationalOrder.order = order
      if (oldValue != null) {
        // TODO: null -> NullEntity later
        oldValue.setProperty(inverseProp.name, null);
      }
      if (newValue != null) {
        newValue.setProperty(inverseProp.name, parent);
      }
    } else {
      // Example: bidirectional navProperty: 1->n: order -> orderDetails
      // orderDetail.order <- newOrder || null
      //    ==> (oldOrder).orderDetails.remove(orderDetail)
      //    ==> order.orderDetails.push(newOrder)
      if (oldValue != null) {
        var oldSiblings = oldValue.getProperty(inverseProp.name);
        var ix = oldSiblings.indexOf(parent);
        if (ix !== -1) {
          oldSiblings.splice(ix, 1);
        }
      }
      if (newValue != null) {
        var siblings = newValue.getProperty(inverseProp.name);
        // recursion check if already in the collection is performed by the relationArray
        siblings.push(parent);
      }
    }
  } else if (property.invForeignKeyNames && entityManager && !entityManager._inKeyFixup) {
    var invForeignKeyNames = property.invForeignKeyNames;
    if (newValue != null) {
      // Example: unidirectional navProperty: 1->1: order -> internationalOrder
      // order.InternationalOrder <- internationalOrder
      //    ==> internationalOrder.orderId = orderId
      //      and
      // Example: unidirectional navProperty: 1->n: order -> orderDetails
      // orderDetail.order <-xxx newOrder
      //    ==> CAN'T HAPPEN because if unidirectional because orderDetail will not have an order prop
      var pkValues = parent.entityAspect.getKey().values;
      invForeignKeyNames.forEach(function (fkName, i) {
        newValue.setProperty(fkName, pkValues[i]);
      });
    } else {
      // Example: unidirectional navProperty: 1->1: order -> internationalOrder
      // order.internationalOrder <- null
      //    ==> (old internationalOrder).orderId = null
      //        and
      // Example: unidirectional navProperty: 1->n: order -> orderDetails
      // orderDetail.order <-xxx newOrder
      //    ==> CAN'T HAPPEN because if unidirectional because orderDetail will not have an order prop
      if (oldValue != null) {
        invForeignKeyNames.forEach(function (fkName) {
          var fkProp = oldValue.entityType.getProperty(fkName);
          if (!fkProp.isPartOfKey) {
            // don't update with null if fk is part of the key
            oldValue.setProperty(fkName, null);
          }
        });
      }
    }
  }

  rawAccessorFn(newValue);

  updateStateAndValidate(context);

  // update fk data property - this can only occur if this navProperty has
  // a corresponding fk on this entity.
  if (property.relatedDataProperties) {
    var entityState = entityAspect.entityState;
    // if either side of nav prop is detached don't clear fks. Note: oldValue in next line cannot be null so no check is needed.
    if (newValue == null && (entityState.isDetached() || oldValue.entityAspect.entityState.isDetached())) return;
    if (entityState.isDeleted()) return;
    var inverseKeyProps = property.entityType.keyProperties;
    inverseKeyProps.forEach(function (keyProp, i) {
      var relatedDataProp = property.relatedDataProperties[i];
      // Do not trash related property if it is part of that entity's key
      if (newValue || !relatedDataProp.isPartOfKey) {
        var relatedValue = newValue ? newValue.getProperty(keyProp.name) : relatedDataProp.defaultValue;
        parent.setProperty(relatedDataProp.name, relatedValue);
      }
    });
  }
}

function postChangeEvents(context) {
  var entityAspect = context.entityAspect;

  var entityManager = entityAspect.entityManager;
  var entity = entityAspect.entity;

  var propChangedArgs = { entity: entity, parent: context.parent, property: context.property, propertyName: context.propertyName, oldValue: context.oldValue, newValue: context.newValue };
  if (entityManager) {
    // propertyChanged will be fired during loading but we only want to fire it once per entity, not once per property.
    // so propertyChanged is fired in the entityManager mergeEntity method if not fired here.
    if ((!entityManager.isLoading) && (!entityManager.isRejectingChanges)) {
      entityAspect.propertyChanged.publish(propChangedArgs);
      // don't fire entityChanged event if propertyChanged is suppressed.
      entityManager.entityChanged.publish({ entityAction: EntityAction.PropertyChange, entity: entity, args: propChangedArgs });
    }
  } else {
    entityAspect.propertyChanged.publish(propChangedArgs);
  }
}

function updateStateAndValidate(context) {
  var entityAspect = context.entityAspect;
  var entityManager = entityAspect.entityManager;
  if (entityManager == null || entityManager.isLoading) return;
  var property = context.property;

if (entityAspect.entityState.isUnchanged() && !property.isUnmapped) {
     entityAspect.setModified();
   }

if (entityManager.validationOptions.validateOnPropertyChange) {
    // entityAspect.entity is NOT the same as parent in the code below. It's use is deliberate.
    entityAspect._validateProperty(context.newValue,
        { entity: entityAspect.entity, property: property, propertyName: context.propertyName, oldValue: context.oldValue });
  }
}

;/**
@module breeze
**/

var DataType = (function () {

  /**
  DataType is an 'Enum' containing all of the supported data types.

  @class DataType
  @static
  **/

  /**
  The default value of this DataType.
  @property defaultValue {any}
  **/

  /**
  Whether this is a 'numeric' DataType.
  @property isNumeric {Boolean}
  **/

  /**
  Whether this is an 'integer' DataType.
  @property isInteger {Boolean}
  **/

  /**
  Function to convert a value from string to this DataType.  Note that this will be called each time a property is changed, so make it fast.
  @method parse {Function}
  @param value {any}
  @param sourceTypeName {String}
  @return value appropriate for this DataType
  **/

  /**
  Function to format this DataType for OData queries.
  @method fmtOData {Function}
  @return value appropriate for OData query
  **/

  /**
  Optional function to get the next value for key generation, if this datatype is used as a key.  Uses an internal table of previous values.
  @method getNext {Function}
  @return value appropriate for this DataType
  **/

  /**
  Optional function to normalize a data value for comparison, if its value cannot be used directly.  Note that this will be called each time a property is changed, so make it fast.
  @method normalize {Function}
  @param value
  @return value appropriate for this DataType
  **/

  /**
  Optional function to get the next value when the datatype is used as a concurrency property.
  @method getConcurrencyValue {Function}
  @param previousValue
  @return the next concurrency value, which may be a function of the previousValue.
  **/

  /**
  Optional function to convert a raw (server) value from string to this DataType.
  @method parseRawValue {Function}
  @param value {any}
  @return value appropriate for this DataType
  **/

  var dataTypeMethods = {
    // default
  };

  var constants;
  var resetConstants = function () {
    constants = {
      stringPrefix: "K_",
      nextNumber: -1,
      nextNumberIncrement: -1
    };
  };

  resetConstants();

  var getNextString = function () {
    return constants.stringPrefix + getNextNumber().toString();
  };

  var getNextNumber = function () {
    var result = constants.nextNumber;
    constants.nextNumber += constants.nextNumberIncrement;
    return result;
  };

  var getNextGuid = function () {
    return __getUuid();
  };

  var getNextDateTime = function () {
    return new Date();
  };

  var getConcurrencyDateTime = function(val) {
    // use the current datetime but insure that it is different from previous call.
    var dt = new Date();
    var dt2 = new Date();
    while (dt.getTime() === dt2.getTime()) {
      dt2 = new Date();
    }
    return dt2;
  };

  var coerceToString = function (source, sourceTypeName) {
    return (source == null) ? source : source.toString();
  };

  var coerceToGuid = function (source, sourceTypeName) {
    if (sourceTypeName === "string") {
      return source.trim().toLowerCase();
    }
    return source;
  };

  var coerceToInt = function (source, sourceTypeName) {
    if (sourceTypeName === "string") {
      var src = source.trim();
      if (src === "") return null;
      var val = parseInt(src, 10);
      return isNaN(val) ? source : val;
    } else if (sourceTypeName === "number") {
      return Math.round(source);
    }
    // do we want to coerce floats -> ints
    return source;
  };

  var coerceToFloat = function (source, sourceTypeName) {
    if (sourceTypeName === "string") {
      var src = source.trim();
      if (src === "") return null;
      var val = parseFloat(src);
      return isNaN(val) ? source : val;
    }
    return source;
  };

  var coerceToDate = function (source, sourceTypeName) {
    var val;
    if (sourceTypeName === "string") {
      var src = source.trim();
      if (src === "") return null;
      val = new Date(Date.parse(src));
      return __isDate(val) ? val : source;
    } else if (sourceTypeName === "number") {
      val = new Date(source);
      return __isDate(val) ? val : source;
    }
    return source;
  };

  var coerceToBool = function (source, sourceTypeName) {
    if (sourceTypeName === "string") {
      var src = source.trim().toLowerCase();
      if (src === "false" || src === "") {
        return false;
      } else if (src === "true") {
        return true;
      } else {
        return source;
      }
    }
    return source;
  };

  var fmtString = function (val) {
    return val == null ? null : "'" + val.replace(/'/g, "''") + "'";
  };

  var fmtInt = function (val) {
    return val == null ? null : ((typeof val === "string") ? parseInt(val, 10) : val);
  };

  var makeFloatFmt = function (fmtSuffix) {
    return function (val) {
      if (val == null) return null;
      if (typeof val === "string") {
        val = parseFloat(val);
      }
      return val + fmtSuffix;
    };
  };

  var fmtDateTime = function (val) {
    if (val == null) return null;
    try {
      return "datetime'" + val.toISOString() + "'";
    } catch (e) {
      throwError("'%1' is not a valid dateTime", val);
    }
  };

  var fmtDateTimeOffset = function (val) {
    if (val == null) return null;
    try {
      return "datetimeoffset'" + val.toISOString() + "'";
    } catch (e) {
      throwError("'%1' is not a valid dateTime", val);
    }
  };

  var fmtTime = function (val) {
    if (val == null) return null;
    if (!__isDuration(val)) {
      throwError("'%1' is not a valid ISO 8601 duration", val);
    }
    return "time'" + val + "'";
  };

  var fmtGuid = function (val) {
    if (val == null) return null;
    if (!__isGuid(val)) {
      throwError("'%1' is not a valid guid", val);
    }
    return "guid'" + val + "'";
  };

  var fmtBoolean = function (val) {
    if (val == null) return null;
    if (typeof val === "string") {
      return val.trim().toLowerCase() === "true";
    } else {
      return !!val;
    }
  };

  var fmtBinary = function (val) {
    if (val == null) return val;
    return "binary'" + val + "'";
  };

  // TODO: __identity;
  var fmtUndefined = function (val) {
    return val;
  };

  function throwError(msg, val) {
    msg = __formatString(msg, val);
    throw new Error(msg);
  }

  var parseRawDate = function(val) {
    if (!__isDate(val)) {
      val = DataType.parseDateFromServer(val);
    }
    return val;
  }

  var parseRawBinary = function(val) {
    if (val && val.$value !== undefined) {
      val = val.$value; // this will be a byte[] encoded as a string
    }
    return val;
  }

  var DataType = new Enum("DataType", dataTypeMethods);


  /**
  @property String {DataType}
  @final
  @static
  **/
  DataType.String = DataType.addSymbol({
    defaultValue: "",
    parse: coerceToString,
    fmtOData: fmtString,
    getNext: getNextString
  });
  /**
  @property Int64 {DataType}
  @final
  @static
  **/
  DataType.Int64 = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    isInteger: true,
    quoteJsonOData: true,
    parse: coerceToInt,
    fmtOData: makeFloatFmt("L"),
    getNext: getNextNumber
  });
  /**
  @property Int32 {DataType}
  @final
  @static
  **/
  DataType.Int32 = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    isInteger: true,
    parse: coerceToInt,
    fmtOData: fmtInt,
    getNext: getNextNumber
  });
  /**
  @property Int16 {DataType}
  @final
  @static
  **/
  DataType.Int16 = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    isInteger: true,
    parse: coerceToInt,
    fmtOData: fmtInt,
    getNext: getNextNumber
  });
  /**
  @property Byte {DataType}
  @final
  @static
  **/
  DataType.Byte = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    isInteger: true,
    parse: coerceToInt,
    fmtOData: fmtInt
  });
  /**
  @property Decimal {DataType}
  @final
  @static
  **/
  DataType.Decimal = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    quoteJsonOData: true,
    isFloat: true,
    parse: coerceToFloat,
    fmtOData: makeFloatFmt("m"),
    getNext: getNextNumber
  });
  /**
  @property Double {DataType}
  @final
  @static
  **/
  DataType.Double = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    isFloat: true,
    parse: coerceToFloat,
    fmtOData: makeFloatFmt("d"),
    getNext: getNextNumber
  });
  /**
  @property Single {DataType}
  @final
  @static
  **/
  DataType.Single = DataType.addSymbol({
    defaultValue: 0,
    isNumeric: true,
    isFloat: true,
    parse: coerceToFloat,
    fmtOData: makeFloatFmt("f"),
    getNext: getNextNumber
  });
  /**
  @property DateTime {DataType}
  @final
  @static
  **/
  DataType.DateTime = DataType.addSymbol({
    defaultValue: new Date(1900, 0, 1),
    isDate: true,
    parse: coerceToDate,
    parseRawValue: parseRawDate,
    normalize: function(value) { return value && value.getTime && value.getTime(); }, // dates don't perform equality comparisons properly
    fmtOData: fmtDateTime,
    getNext: getNextDateTime,
    getConcurrencyValue: getConcurrencyDateTime
  });

  /**
  @property DateTimeOffset {DataType}
  @final
  @static
  **/
  DataType.DateTimeOffset = DataType.addSymbol({
    defaultValue: new Date(1900, 0, 1),
    isDate: true,
    parse: coerceToDate,
    parseRawValue: parseRawDate,
    normalize: function (value) { return value && value.getTime && value.getTime(); }, // dates don't perform equality comparisons properly
    fmtOData: fmtDateTimeOffset,
    getNext: getNextDateTime,
    getConcurrencyValue: getConcurrencyDateTime
  });
  /**
  @property Time {DataType}
  @final
  @static
  **/
  DataType.Time = DataType.addSymbol({
    defaultValue: "PT0S",
    fmtOData: fmtTime,
    parseRawValue: DataType.parseTimeFromServer
  });
  /**
  @property Boolean {DataType}
  @final
  @static
  **/
  DataType.Boolean = DataType.addSymbol({
    defaultValue: false,
    parse: coerceToBool,
    fmtOData: fmtBoolean
  });
  /**
  @property Guid {DataType}
  @final
  @static
  **/
  DataType.Guid = DataType.addSymbol({
    defaultValue: "00000000-0000-0000-0000-000000000000",
    parse: coerceToGuid,
    fmtOData: fmtGuid,
    getNext: getNextGuid,
    parseRawValue: function(val) { return val.toLowerCase(); },
    getConcurrencyValue: __getUuid
  });

  /**
  @property Binary {DataType}
  @final
  @static
  **/
  DataType.Binary = DataType.addSymbol({
    defaultValue: null,
    fmtOData: fmtBinary,
    parseRawValue: parseRawBinary
  });
  /**
  @property Undefined {DataType}
  @final
  @static
  **/
  DataType.Undefined = DataType.addSymbol({
    defaultValue: undefined,
    fmtOData: fmtUndefined
  });
  DataType.resolveSymbols();

  DataType.getComparableFn = function(dataType) {
    if (dataType && dataType.normalize) {
      return dataType.normalize;
    } else if (dataType === DataType.Time) {
      // durations must be converted to compare them
      return function (value) {
        return value && __durationToSeconds(value);
      };
    } else {
      // TODO: __identity
      return function (value) {
        return value;
      };
    }
  };

  /**
  Returns the DataType for a specified EDM type name.
  @method fromEdmDataType
  @static
  @param typeName {String}
  @return {DataType} A DataType.
  **/
  DataType.fromEdmDataType = function (typeName) {
    var dt = null;
    var parts = typeName.split(".");
    if (parts.length > 1) {
      var simpleName = parts[1];
      if (simpleName === "image") {
        // hack
        dt = DataType.Byte;
      } else if (parts.length === 2) {
        dt = DataType.fromName(simpleName) || DataType.Undefined;
      } else {
        // enum
        // dt = DataType.Int32;
        dt = DataType.String;
      }
    }

    return dt;
  };

  DataType.fromValue = function (val) {
    if (__isDate(val)) return DataType.DateTime;
    switch (typeof val) {
      case "string":
        if (__isGuid(val)) return DataType.Guid;
        // the >3 below is a hack to insure that if we are inferring datatypes that
        // very short strings that are valid but unlikely ISO encoded Time's are treated as strings instead.
        else if (__isDuration(val) && val.length > 3) return DataType.Time;
        else if (__isDateString(val)) return DataType.DateTime;
        return DataType.String;
      case "boolean":
        return DataType.Boolean;
      case "number":
        return DataType.Double;
    }
    return DataType.Undefined;
  };

  var _localTimeRegex = /.\d{3}$/;

  DataType.parseTimeFromServer = function (source) {
    if (typeof source === 'string') {
      return source;
    }
    // ODATA v3 format
    if (source && source.__edmType === 'Edm.Time') {
      var seconds = Math.floor(source.ms / 1000);
      return 'PT' + seconds + 'S';
    }
    return source;
  }

  DataType.parseDateAsUTC = function (source) {
    if (typeof source === 'string') {
      // convert to UTC string if no time zone specifier.
      var isLocalTime = _localTimeRegex.test(source);
      // var isLocalTime = !hasTimeZone(source);
      source = isLocalTime ? source + 'Z' : source;
    }
    source = new Date(Date.parse(source));
    return source;
  };

  //function hasTimeZone(source) {
  //  var ix = source.indexOf("T");
  //  var timePart = source.substring(ix+1);
  //  return  timePart.indexOf("-") >= 0 || timePart.indexOf("+") >= 0 || timePart.indexOf("Z");
  //}

  // NOT YET NEEDED --------------------------------------------------
  // var _utcOffsetMs = (new Date()).getTimezoneOffset() * 60000;

  //DataType.parseDateAsLocal = function (source) {
  //    var dt = DataType.parseDatesAsUTC(source);
  //    if (__isDate(dt)) {
  //        dt = new Date(dt.getTime() + _utcOffsetMs);
  //    }
  //    return dt;
  //};
  // -----------------------------------------------------------------

  DataType.parseDateFromServer = DataType.parseDateAsUTC;

  DataType.parseRawValue = function (val, dataType) {
    // undefined values will be the default for most unmapped properties EXCEPT when they are set
    // in a jsonResultsAdapter ( an unusual use case).
    if (val === undefined) return undefined;
    if (!val) return val;
    if (dataType && dataType.parseRawValue) {
      val = dataType.parseRawValue(val);
    }
    return val;
  }

  DataType.constants = constants;
  // for internal testing only
  DataType._resetConstants = resetConstants;

  DataType.getSymbols().forEach(function (sym) {
    sym.validatorCtor = getValidatorCtor(sym);
  });

  function getValidatorCtor(symbol) {
    switch (symbol) {
      case DataType.String:
        return Validator.string;
      case DataType.Int64:
        return Validator.int64;
      case DataType.Int32:
        return Validator.int32;
      case DataType.Int16:
        return Validator.int16;
      case DataType.Decimal:
        return Validator.number;
      case DataType.Double:
        return Validator.number;
      case DataType.Single:
        return Validator.number;
      case DataType.DateTime:
        return Validator.date;
      case DataType.DateTimeOffset:
        return Validator.date;
      case DataType.Boolean:
        return Validator.bool;
      case DataType.Guid:
        return Validator.guid;
      case DataType.Byte:
        return Validator.byte;
      case DataType.Binary:
        // TODO: don't quite know how to validate this yet.
        return Validator.none;
      case DataType.Time:
        return Validator.duration;
      case DataType.Undefined:
        return Validator.none;
    }
  }

  return DataType;

})();

breeze.DataType = DataType;

;/**
@module breeze
**/

var DataService = (function () {
  
  /**
  A DataService instance is used to encapsulate the details of a single 'service'; this includes a serviceName, a dataService adapterInstance,
  and whether the service has server side metadata.

  You can construct an EntityManager with either a serviceName or a DataService instance, if you use a serviceName then a DataService
  is constructed for you.  (It can also be set via the EntityManager.setProperties method).

  The same applies to the MetadataStore.fetchMetadata method, i.e. it takes either a serviceName or a DataService instance.

  Each metadataStore contains a list of DataServices, each accessible via its ‘serviceName’.
  ( see MetadataStore.getDataService and MetadataStore.addDataService).  The ‘addDataService’ method is called internally
  anytime a MetadataStore.fetchMetadata call occurs with a new dataService ( or service name).
  @class DataService
  **/

  /**
  DataService constructor

  @example
      var dataService = new DataService({
          serviceName: altServiceName,
          hasServerMetadata: false
      });

      var metadataStore = new MetadataStore({
          namingConvention: NamingConvention.camelCase
      });

      return new EntityManager({
          dataService: dataService,
          metadataStore: metadataStore
      });

  @method <ctor> DataService
  @param config {Object}
  @param config.serviceName {String} The name of the service.
  @param [config.adapterName] {String} The name of the dataServiceAdapter to be used with this service.
  @param [config.uriBuilderName] {String} The name of the uriBuilder to be used with this service.
  @param [config.hasServerMetadata] {bool} Whether the server can provide metadata for this service.
  @param [config.jsonResultsAdapter] {JsonResultsAdapter}  The JsonResultsAdapter used to process the results of any query against this service.
  @param [config.useJsonp] {Boolean}  Whether to use JSONP when making a 'get' request against this service.
  **/
  var ctor = function DataService(config) {
    // this.uriBuilder = uriBuilderForOData;
    updateWithConfig(this, config);
  };
  var proto = ctor.prototype;
  proto._$typeName = "DataService";
  
  /**
  The serviceName for this DataService.

  __readOnly__
  @property serviceName {String}
  **/

  /**
  The adapter name for the dataServiceAdapter to be used with this service.

  __readOnly__
  @property adapterName {String}
  **/

  /**
  The "dataService" adapter implementation instance associated with this EntityManager.

  __readOnly__
  @property adapterInstance {an instance of the "dataService" adapter interface}
  **/

  /**
  Whether the server can provide metadata for this service.

  __readOnly__
  @property hasServerMetadata {Boolean}
  **/

  /**
  The JsonResultsAdapter used to process the results of any query against this DataService.

  __readOnly__
  @property jsonResultsAdapter {JsonResultsAdapter}
  **/

  /**
  Whether to use JSONP when performing a 'GET' request against this service.

  __readOnly__
  @property useJsonP {Boolean}
  **/

  /**
  Returns a copy of this DataService with the specified properties applied.
  @method using
  @param config {Configuration Object} The object to apply to create a new DataService.
  @return {DataService}
  @chainable
  **/
  proto.using = function (config) {
    if (!config) return this;
    var result = new DataService(this);
    return updateWithConfig(result, config);
  };
  
  ctor.resolve = function (dataServices) {
    // final defaults
    dataServices.push({
      hasServerMetadata: true,
      useJsonp: false
    });
    var ds = new DataService(__resolveProperties(dataServices,
        ["serviceName", "adapterName", "uriBuilderName", "hasServerMetadata", "jsonResultsAdapter", "useJsonp"]));
    
    if (!ds.serviceName) {
      throw new Error("Unable to resolve a 'serviceName' for this dataService");
    }
    ds.adapterInstance = ds.adapterInstance || __config.getAdapterInstance("dataService", ds.adapterName);
    ds.jsonResultsAdapter = ds.jsonResultsAdapter || ds.adapterInstance.jsonResultsAdapter;
    ds.uriBuilder = ds.uriBuilder || __config.getAdapterInstance("uriBuilder", ds.uriBuilderName);
    return ds;
  };
  
  function updateWithConfig(obj, config) {
    if (config) {
      assertConfig(config)
          .whereParam("serviceName").isOptional()
          .whereParam("adapterName").isString().isOptional()
          .whereParam("uriBuilderName").isString().isOptional()
          .whereParam("hasServerMetadata").isBoolean().isOptional()
          .whereParam("jsonResultsAdapter").isInstanceOf(JsonResultsAdapter).isOptional()
          .whereParam("useJsonp").isBoolean().isOptional()
          .applyAll(obj);
      obj.serviceName = obj.serviceName && DataService._normalizeServiceName(obj.serviceName);
      obj.adapterInstance = obj.adapterName && __config.getAdapterInstance("dataService", obj.adapterName);
      obj.uriBuilder = obj.uriBuilderName && __config.getAdapterInstance("uriBuilder", obj.uriBuilderName);
    }
    return obj;
  }
  
  ctor._normalizeServiceName = function (serviceName) {
    serviceName = serviceName.trim();
    if (serviceName.substr(-1) !== "/") {
      return serviceName + '/';
    } else {
      return serviceName;
    }
  };
  
  proto.toJSON = function () {
    // don't use default value here - because we want to be able to distinguish undefined props for inheritence purposes.
    return __toJson(this, {
      serviceName: null,
      adapterName: null,
      uriBuilderName: null,
      hasServerMetadata: null,
      jsonResultsAdapter: function (v) {
        return v && v.name;
      },
      useJsonp: null
    });
  };
  
  ctor.fromJSON = function (json) {
    json.jsonResultsAdapter = __config._fetchObject(JsonResultsAdapter, json.jsonResultsAdapter);
    return new DataService(json);
  };

  /**
   Returns a url for this dataService with the specified suffix. This method handles dataService names either
   with or without trailing '/'s.
   @method qualifyUrl
   @param suffix {String} The resulting url.
   @return {a Url string}
   **/
  proto.qualifyUrl = function (suffix) {
    var url = this.serviceName;
    // remove any trailing "/"
    if (core.stringEndsWith(url, "/")) {
      url = url.substr(0, url.length - 1);
    }
    // ensure that it ends with "/" + suffix
    suffix = "/" + suffix;
    if (!core.stringEndsWith(url, suffix)) {
      url = url + suffix;
    }
    return url;
  };
  
  return ctor;
})();

var JsonResultsAdapter = (function () {
  /**
  A JsonResultsAdapter instance is used to provide custom extraction and parsing logic on the json results returned by any web service.
  This facility makes it possible for breeze to talk to virtually any web service and return objects that will be first class 'breeze' citizens.

  @class JsonResultsAdapter
  **/

  /**
  JsonResultsAdapter constructor

  @example
      //
      var jsonResultsAdapter = new JsonResultsAdapter({
          name: "test1e",
          extractResults: function(json) {
              return json.results;
          },
          visitNode: function(node, mappingContext, nodeContext) {
              var entityType = normalizeTypeName(node.$type);
              var propertyName = nodeContext.propertyName;
              var ignore = propertyName && propertyName.substr(0, 1) === "$";

              return {
                  entityType: entityType,
                  nodeId: node.$id,
                  nodeRefId: node.$ref,
                  ignore: ignore,
                  passThru: false // default
              };
          }
      });

      var dataService = new DataService( {
              serviceName: "breeze/foo",
              jsonResultsAdapter: jsonResultsAdapter
      });

      var entityManager = new EntityManager( {
          dataService: dataService
      });

  @method <ctor> JsonResultsAdapter
  @param config {Object}
  @param config.name {String} The name of this adapter.  This name is used to uniquely identify and locate this instance when an 'exported' JsonResultsAdapter is later imported.
  @param [config.extractResults] {Function} Called once per query operation to extract the 'payload' from any json received over the wire.
  This method has a default implementation which to simply return the "results" property from any json returned as a result of executing the query.
  @param [config.extractSaveResults] {Function} Called once per save operation to extract the entities from any json received over the wire.  Must return an array.
  This method has a default implementation which to simply return the "entities" property from any json returned as a result of executing the save.
  @param [config.extractKeyMappings] {Function} Called once per save operation to extract the key mappings from any json received over the wire.  Must return an array.
  This method has a default implementation which to simply return the "keyMappings" property from any json returned as a result of executing the save.
  @param config.visitNode {Function} A visitor method that will be called on each node of the returned payload.
  **/
  var ctor = function JsonResultsAdapter(config) {
    if (arguments.length !== 1) {
      throw new Error("The JsonResultsAdapter ctor should be called with a single argument that is a configuration object.");
    }
    
    assertConfig(config)
        .whereParam("name").isNonEmptyString()
        .whereParam("extractResults").isFunction().isOptional().withDefault(extractResultsDefault)
        .whereParam("extractSaveResults").isFunction().isOptional().withDefault(extractSaveResultsDefault)
        .whereParam("extractKeyMappings").isFunction().isOptional().withDefault(extractKeyMappingsDefault)
        .whereParam("extractDeletedKeys").isFunction().isOptional().withDefault(extractDeletedKeysDefault)
        .whereParam("visitNode").isFunction()
        .applyAll(this);
    __config._storeObject(this, proto._$typeName, this.name);
  };
  
  var proto = ctor.prototype;
  proto._$typeName = "JsonResultsAdapter";
  
  function extractResultsDefault(data) {
    return data.results;
  }

  function extractSaveResultsDefault(data) {
    return data.entities || data.Entities || [];
  }
  
  function extractKeyMappingsDefault(data) {
    return data.keyMappings || data.KeyMappings || [];
  }

  function extractDeletedKeysDefault(data) {
    return data.deletedKeys || data.DeletedKeys || [];
  }

  return ctor;
})();

breeze.DataService = DataService;
breeze.JsonResultsAdapter = JsonResultsAdapter;


;/**
@module breeze
**/

// Get the promises library called Q
// define a quick failing version if not found.
var Q = core.requireLib("Q;q");

if (!Q) {
  // No Q.js! Substitute a placeholder Q which always fails
  // Should be replaced by the app via breeze.config.setQ
  // For example, see Breeze Labs "breeze.angular"
  Q = function () {
    var eMsg = 'Q is undefined. Are you missing Q.js? See https://github.com/kriskowal/q';
    throw new Error(eMsg);
  }

  // all Q methods called by Breeze should fail
  Q.defer = Q.resolve = Q.reject = Q;
}

/**
 (Re)set Q with a promises implementation suitable for Breeze internal use.  Note: This API is likely to change.
 @method setQ
 @param q {Object} - a  "thenable" promises implementation like Q.js with the API that Breeze requires internally.
 @param [q.defer] {Function} A function returning a deferred.
 @param [q.resolve] {Function} A function returning a resolved promise.
 @param [q.reject] {Function} A function returning a rejected promise.
 **/
breeze.config.setQ = function (q) {
  breeze.Q = Q = q;
}
breeze.Q = Q; // Todo: consider a "safer" way for apps to get breeze's Q. 


var MetadataStore = (function () {

  /**
  An instance of the MetadataStore contains all of the metadata about a collection of {{#crossLink "EntityType"}}{{/crossLink}}'s.
  MetadataStores may be shared across {{#crossLink "EntityManager"}}{{/crossLink}}'s.  If an EntityManager is created without an
  explicit MetadataStore, the MetadataStore from the MetadataStore.defaultInstance property will be used.
  @class MetadataStore
  **/

  var __id = 0;

  /**
  Constructs a new MetadataStore.
  @example
      var ms = new MetadataStore();
  The store can then be associated with an EntityManager
  @example
      var entityManager = new EntityManager( {
          serviceName: "breeze/NorthwindIBModel", 
          metadataStore: ms 
      });
  or for an existing EntityManager
  @example
      // Assume em1 is an existing EntityManager
      em1.setProperties( { metadataStore: ms });
  @method <ctor> MetadataStore
  @param [config] {Object} Configuration settings .
  @param [config.namingConvention=NamingConvention.defaultInstance] {NamingConvention} NamingConvention to be used in mapping property names
  between client and server. Uses the NamingConvention.defaultInstance if not specified.
  @param [config.localQueryComparisonOptions=LocalQueryComparisonOptions.defaultInstance] {LocalQueryComparisonOptions} The LocalQueryComparisonOptions to be
  used when performing "local queries" in order to match the semantics of queries against a remote service.
  @param [config.serializerFn] A function that is used to mediate the serialization of instances of this type.
  **/
  var ctor = function MetadataStore(config) {
    config = config || { };
    assertConfig(config)
        .whereParam("namingConvention").isOptional().isInstanceOf(NamingConvention).withDefault(NamingConvention.defaultInstance)
        .whereParam("localQueryComparisonOptions").isOptional().isInstanceOf(LocalQueryComparisonOptions).withDefault(LocalQueryComparisonOptions.defaultInstance)
        .whereParam("serializerFn").isOptional().isFunction()
        .applyAll(this);
    this.dataServices = []; // array of dataServices;
    this._resourceEntityTypeMap = {}; // key is resource name - value is qualified entityType name
    this._structuralTypeMap = {}; // key is qualified structuraltype name - value is structuralType. ( structural = entityType or complexType).
    this._shortNameMap = {}; // key is shortName, value is qualified name - does not need to be serialized.
    this._ctorRegistry = {}; // key is either short or qual type name - value is ctor;
    this._incompleteTypeMap = {}; // key is entityTypeName; value is array of nav props
    this._incompleteComplexTypeMap = {}; // key is complexTypeName; value is array of complexType props
    this._id = __id++;
    this.metadataFetched = new Event("metadataFetched", this);

  };
  var proto = ctor.prototype;
  proto._$typeName = "MetadataStore";
  Event.bubbleEvent(proto, null);
  ctor.ANONTYPE_PREFIX = "_IB_";


  // needs to be made avail to breeze.dataService.xxx files
  ctor.normalizeTypeName = __memoize(function (rawTypeName) {
    return rawTypeName && parseTypeName(rawTypeName).typeName;
  });
  // for debugging use the line below instead.
  //ctor.normalizeTypeName = function (rawTypeName) { return parseTypeName(rawTypeName).typeName; };

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires after a MetadataStore has completed fetching metadata from a remote service.

  @example
      var ms = myEntityManager.metadataStore;
      ms.metadataFetched.subscribe(function(args) {
              var metadataStore = args.metadataStore;
              var dataService = args.dataService;
          });
      });

  @event metadataFetched
  @param metadataStore {MetadataStore} The MetadataStore into which the metadata was fetched.
  @param dataService {DataService} The DataService that metadata was fetched from.
  @param rawMetadata {Object} The raw metadata returned from the service. (It will have already been processed by this point).
  @readOnly
  **/

  /**
  General purpose property set method
  @example
      // assume em1 is an EntityManager containing a number of existing entities.

      em1.metadataStore.setProperties( {
          version: "6.1.3",
          serializerFn: function(prop, value) {
          return (prop.isUnmapped) ? undefined : value;
          }
      )};
  @method setProperties
  @param config [object]
  @param [config.name] {String} A name for the collection of metadata in this store.
  @param [config.serializerFn] A function that is used to mediate the serialization of instances of this type.
  **/
  proto.setProperties = function (config) {
    assertConfig(config)
        .whereParam("name").isString().isOptional()
        .whereParam("serializerFn").isFunction().isOptional()
        .applyAll(this);
  };

  /**
  Adds a DataService to this MetadataStore. If a DataService with the same serviceName is already
  in the MetadataStore an exception will be thrown.
  @method addDataService
  @param dataService {DataService} The DataService to add
  @param [shouldOverwrite=false] {Boolean} Permit overwrite of existing DataService rather than throw exception
  **/
  proto.addDataService = function (dataService, shouldOverwrite) {
    assertParam(dataService, "dataService").isInstanceOf(DataService).check();
    assertParam(shouldOverwrite, "shouldOverwrite").isBoolean().isOptional().check();
    var ix = this._getDataServiceIndex(dataService.serviceName);
    if (ix >= 0) {
      if (!!shouldOverwrite) {
        this.dataServices[ix] = dataService;
      } else {
        throw new Error("A dataService with this name '" + dataService.serviceName + "' already exists in this MetadataStore");
      }
    } else {
      this.dataServices.push(dataService);
    }
  };

  proto._getDataServiceIndex = function (serviceName) {
    return __arrayIndexOf(this.dataServices, function (ds) {
      return ds.serviceName === serviceName;
    });
  };

  /**
  Adds an EntityType to this MetadataStore.  No additional properties may be added to the EntityType after its has
  been added to the MetadataStore.
  @method addEntityType
  @param structuralType {EntityType|ComplexType} The EntityType or ComplexType to add
  **/
  proto.addEntityType = function (structuralType) {
    if (!(structuralType instanceof EntityType || structuralType instanceof ComplexType)) {
      structuralType = structuralType.isComplexType ? new ComplexType(structuralType) : new EntityType(structuralType);
    }

    if (!structuralType.isComplexType) {
      if (structuralType.baseTypeName && !structuralType.baseEntityType) {
        var baseEntityType = this._getEntityType(structuralType.baseTypeName, true);
        structuralType._updateFromBase(baseEntityType);
      }
      if (structuralType.keyProperties.length === 0 && !structuralType.isAbstract) {
        throw new Error("Unable to add " + structuralType.name +
            " to this MetadataStore.  An EntityType must have at least one property designated as a key property - See the 'DataProperty.isPartOfKey' property.");
      }
    }

    structuralType.metadataStore = this;
    // don't register anon types
    if (!structuralType.isAnonymous) {
      if (this._structuralTypeMap[structuralType.name]) {
        throw new Error("Type " + structuralType.name + " already exists in this MetadataStore.");
      }

      this._structuralTypeMap[structuralType.name] = structuralType;
      this._shortNameMap[structuralType.shortName] = structuralType.name;
    }

    structuralType.getProperties().forEach(function (property) {
      structuralType._updateNames(property);
      if (!property.isUnmapped) {
        structuralType._mappedPropertiesCount++;
      }
    });

    structuralType._updateCps();

    if (!structuralType.isComplexType) {
      structuralType._updateNps();
      // give the type it's base's resource name if it doesn't have its own.
      var defResourceName = structuralType.defaultResourceName || (structuralType.baseEntityType && structuralType.baseEntityType.defaultResourceName);
      if (defResourceName && !this.getEntityTypeNameForResourceName(defResourceName)) {
        this.setEntityTypeForResourceName(defResourceName, structuralType.name);
      }
      structuralType.defaultResourceName = defResourceName;
      // check if this structural type's name, short version or qualified version has a registered ctor.
      structuralType.getEntityCtor();
    }

  };

  /**
  The  {{#crossLink "NamingConvention"}}{{/crossLink}} associated with this MetadataStore.

  __readOnly__
  @property namingConvention {NamingConvention}
  **/

  /**
  Exports this MetadataStore to a serialized string appropriate for local storage.   This operation is also called
  internally when exporting an EntityManager.
  @example
      // assume ms is a previously created MetadataStore
      var metadataAsString = ms.exportMetadata();
      window.localStorage.setItem("metadata", metadataAsString);
      // and later, usually in a different session imported
      var metadataFromStorage = window.localStorage.getItem("metadata");
      var newMetadataStore = new MetadataStore();
      newMetadataStore.importMetadata(metadataFromStorage);
  @method exportMetadata
  @return {String} A serialized version of this MetadataStore that may be stored locally and later restored.
  **/
  proto.exportMetadata = function () {
    var result = JSON.stringify({
      "metadataVersion": breeze.metadataVersion,
      "name": this.name,
      "namingConvention": this.namingConvention.name,
      "localQueryComparisonOptions": this.localQueryComparisonOptions.name,
      "dataServices": this.dataServices,
      "structuralTypes": __objectMap(this._structuralTypeMap),
      "resourceEntityTypeMap": this._resourceEntityTypeMap
    }, null, __config.stringifyPad);
    return result;
  };

  /**
  Imports a previously exported serialized MetadataStore into this MetadataStore.
  @example
      // assume ms is a previously created MetadataStore
      var metadataAsString = ms.exportMetadata();
      window.localStorage.setItem("metadata", metadataAsString);
      // and later, usually in a different session
      var metadataFromStorage = window.localStorage.getItem("metadata");
      var newMetadataStore = new MetadataStore();
      newMetadataStore.importMetadata(metadataFromStorage);
  @method importMetadata
  @param exportedMetadata {String|JSON Object} A previously exported MetadataStore.
  @param [allowMerge] {Boolean} Allows custom metadata to be merged into existing metadata types.
  @return {MetadataStore} This MetadataStore.
  @chainable
  **/
  proto.importMetadata = function (exportedMetadata, allowMerge) {
    assertParam(allowMerge, "allowMerge").isOptional().isBoolean().check();
    this._deferredTypes = {};
    var json = (typeof (exportedMetadata) === "string") ? JSON.parse(exportedMetadata) : exportedMetadata;

    if (json.schema) {
      return CsdlMetadataParser.parse(this, json.schema, json.altMetadata);
    }

    if (json.metadataVersion && json.metadataVersion !== breeze.metadataVersion) {
      var msg = __formatString("Cannot import metadata with a different 'metadataVersion' (%1) than the current 'breeze.metadataVersion' (%2) ",
          json.metadataVersion, breeze.metadataVersion);
      throw new Error(msg);
    }

    var ncName = json.namingConvention;
    var lqcoName = json.localQueryComparisonOptions;
    if (this.isEmpty()) {
      this.namingConvention = __config._fetchObject(NamingConvention, ncName) || this.namingConvention;
      this.localQueryComparisonOptions = __config._fetchObject(LocalQueryComparisonOptions, lqcoName) || this.localQueryComparisonOptions;
    } else {
      if (ncName && this.namingConvention.name !== ncName) {
        throw new Error("Cannot import metadata with a different 'namingConvention' from the current MetadataStore");
      }
      if (lqcoName && this.localQueryComparisonOptions.name !== lqcoName) {
        throw new Error("Cannot import metadata with different 'localQueryComparisonOptions' from the current MetadataStore");
      }
    }

    var that = this;

    //noinspection JSHint
    json.dataServices && json.dataServices.forEach(function (ds) {
      ds = DataService.fromJSON(ds);
      that.addDataService(ds, true);
    });
    var structuralTypeMap = this._structuralTypeMap;

    json.structuralTypes && json.structuralTypes.forEach(function (stype) {
      structuralTypeFromJson(that, stype, allowMerge);
    });
    __extend(this._resourceEntityTypeMap, json.resourceEntityTypeMap);
    __extend(this._incompleteTypeMap, json.incompleteTypeMap);

    return this;
  };

  /**
  Creates a new MetadataStore from a previously exported serialized MetadataStore
  @example
      // assume ms is a previously created MetadataStore
      var metadataAsString = ms.exportMetadata();
      window.localStorage.setItem("metadata", metadataAsString);
      // and later, usually in a different session
      var metadataFromStorage = window.localStorage.getItem("metadata");
      var newMetadataStore = MetadataStore.importMetadata(metadataFromStorage);
  @method importMetadata
  @static
  @param exportedString {String} A previously exported MetadataStore.
  @return {MetadataStore} A new MetadataStore.

  **/
  ctor.importMetadata = function (exportedString) {
    var ms = new MetadataStore();
    ms.importMetadata(exportedString);
    return ms;
  };

  /**
  Returns whether Metadata has been retrieved for a specified service name.
  @example
      // Assume em1 is an existing EntityManager.
      if (!em1.metadataStore.hasMetadataFor("breeze/NorthwindIBModel"))) {
          // do something interesting
      }
  @method hasMetadataFor
  @param serviceName {String} The service name.
  @return {Boolean}
  **/
  proto.hasMetadataFor = function (serviceName) {
    return !!this.getDataService(serviceName);
  };

  /**
  Returns the DataService for a specified service name
  @example
      // Assume em1 is an existing EntityManager.
      var ds = em1.metadataStore.getDataService("breeze/NorthwindIBModel");
      var adapterName = ds.adapterName; // may be null

  @method getDataService
  @param serviceName {String} The service name.
  @return {DataService}
  **/
  proto.getDataService = function (serviceName) {
    assertParam(serviceName, "serviceName").isString().check();

    serviceName = DataService._normalizeServiceName(serviceName);
    return __arrayFirst(this.dataServices, function (ds) {
      return ds.serviceName === serviceName;
    });
  };

  /**
  Fetches the metadata for a specified 'service'. This method is automatically called
  internally by an EntityManager before its first query against a new service.

  @example
  Usually you will not actually process the results of a fetchMetadata call directly, but will instead
  ask for the metadata from the EntityManager after the fetchMetadata call returns.
  @example
      var ms = new MetadataStore();
      // or more commonly
      // var ms = anEntityManager.metadataStore;
      ms.fetchMetadata("breeze/NorthwindIBModel").then(function(rawMetadata) {
            // do something with the metadata
      }).fail(function(exception) {
          // handle exception here
      });
  @method fetchMetadata
  @async
  @param dataService {DataService|String}  Either a DataService or just the name of the DataService to fetch metadata for.

  @param [callback] {Function} Function called on success.

  successFunction([data])
  @param [callback.data] {rawMetadata}

  @param [errorCallback] {Function} Function called on failure.

  failureFunction([error])
  @param [errorCallback.error] {Error} Any error that occured wrapped into an Error object.

  @return {Promise} Promise
  **/
  proto.fetchMetadata = function (dataService, callback, errorCallback) {
    try {
      assertParam(dataService, "dataService").isString().or().isInstanceOf(DataService).check();
      assertParam(callback, "callback").isFunction().isOptional().check();
      assertParam(errorCallback, "errorCallback").isFunction().isOptional().check();

      if (typeof dataService === "string") {
        // use the dataService with a matching name or create a new one.
        dataService = this.getDataService(dataService) || new DataService({ serviceName: dataService });
      }

      dataService = DataService.resolve([dataService]);


      if (this.hasMetadataFor(dataService.serviceName)) {
        throw new Error("Metadata for a specific serviceName may only be fetched once per MetadataStore. ServiceName: " + dataService.serviceName);
      }
      var that = this;
      return dataService.adapterInstance.fetchMetadata(this, dataService).then(function (rawMetadata) {
        that.metadataFetched.publish({ metadataStore: that, dataService: dataService, rawMetadata: rawMetadata });
        if (callback) callback(rawMetadata);
        return Q.resolve(rawMetadata);
      }, function (error) {
        if (errorCallback) errorCallback(error);
        return Q.reject(error);
      });
    } catch (e) {
      return Q.reject(e);
    }
  };


  /**
  Used to register a constructor for an EntityType that is not known via standard Metadata discovery;
  i.e. an unmapped type.

  @method trackUnmappedType
  @param entityCtor {Function} The constructor for the 'unmapped' type.
  @param [interceptor] {Function} A function
  **/
  proto.trackUnmappedType = function (entityCtor, interceptor) {
    assertParam(entityCtor, "entityCtor").isFunction().check();
    assertParam(interceptor, "interceptor").isFunction().isOptional().check();
    // TODO: think about adding this to the MetadataStore.
    var entityType = new EntityType(this);
    entityType._setCtor(entityCtor, interceptor);
  };

  /**
  Provides a mechanism to register a 'custom' constructor to be used when creating new instances
  of the specified entity type.  If this call is not made, a default constructor is created for
  the entity as needed.
  This call may be made before or after the corresponding EntityType has been discovered via
  Metadata discovery.
  @example
      var Customer = function () {
              this.miscData = "asdf";
          };
      Customer.prototype.doFoo() {
              ...
          }
      // assume em1 is a preexisting EntityManager;
      em1.metadataStore.registerEntityTypeCtor("Customer", Customer);
      // any queries or EntityType.create calls from this point on will call the Customer constructor
      // registered above.
  @method registerEntityTypeCtor
  @param structuralTypeName {String} The name of the EntityType or ComplexType.
  @param aCtor {Function}  The constructor for this EntityType or ComplexType; may be null if all you want to do is set the next parameter.
  @param [initFn] {Function} A function or the name of a function on the entity that is to be executed immediately after the entity has been created
  and populated with any initial values.
  initFn(entity)
  @param initFn.entity {Entity} The entity being created or materialized.
  @param [noTrackingFn] {Function} A function that is executed immediately after a noTracking entity has been created and whose return
  value will be used in place of the noTracking entity.
  @param noTrackingFn.entity {Object}
  @param noTrackingFn.entityType {EntityType} The entityType that the 'entity' parameter would be if we were tracking
  **/
  proto.registerEntityTypeCtor = function (structuralTypeName, aCtor, initFn, noTrackingFn) {
    assertParam(structuralTypeName, "structuralTypeName").isString().check();
    assertParam(aCtor, "aCtor").isFunction().isOptional().check();
    assertParam(initFn, "initFn").isOptional().isFunction().or().isString().check();
    assertParam(noTrackingFn, "noTrackingFn").isOptional().isFunction().check();

    var qualifiedTypeName = getQualifiedTypeName(this, structuralTypeName, false);
    var typeName = qualifiedTypeName || structuralTypeName;

    if (aCtor) {
      if (aCtor._$typeName && aCtor._$typeName != typeName) {
        console.warn("Registering a constructor for " + typeName + " that is already used for " + aCtor._$typeName + ".");
      }
      aCtor._$typeName = typeName;
    }

    this._ctorRegistry[typeName] = { ctor: aCtor, initFn: initFn, noTrackingFn: noTrackingFn };
    if (qualifiedTypeName) {
      var stype = this._structuralTypeMap[qualifiedTypeName];
      stype && stype.getCtor(true); // this will complete the registration if avail now.
    }

  };

  /**
  Returns whether this MetadataStore contains any metadata yet.
  @example
      // assume em1 is a preexisting EntityManager;
      if (em1.metadataStore.isEmpty()) {
          // do something interesting
      }
  @method isEmpty
  @return {Boolean}
  **/
  proto.isEmpty = function () {
    return __isEmpty(this._structuralTypeMap);
  };

  /**
  Returns an  {{#crossLink "EntityType"}}{{/crossLink}} or a {{#crossLink "ComplexType"}}{{/crossLink}} given its name.
  @example
      // assume em1 is a preexisting EntityManager
      var odType = em1.metadataStore.getEntityType("OrderDetail");
  or to throw an error if the type is not found
  @example
      var badType = em1.metadataStore.getEntityType("Foo", false);
      // badType will not get set and an exception will be thrown.
  @method getEntityType
  @param structuralTypeName {String}  Either the fully qualified name or a short name may be used. If a short name is specified and multiple types share
  that same short name an exception will be thrown.
  @param [okIfNotFound=false] {Boolean} Whether to throw an error if the specified EntityType is not found.
  @return {EntityType|ComplexType} The EntityType. ComplexType or 'undefined' if not not found.
  **/
  proto.getEntityType = function (structuralTypeName, okIfNotFound) {
    assertParam(structuralTypeName, "structuralTypeName").isString().check();
    assertParam(okIfNotFound, "okIfNotFound").isBoolean().isOptional().check(false);
    return this._getEntityType(structuralTypeName, okIfNotFound);
  };

  proto._getEntityType = function (typeName, okIfNotFound) {
    var qualTypeName = getQualifiedTypeName(this, typeName, false);
    var type = this._structuralTypeMap[qualTypeName];
    if (!type) {
      if (okIfNotFound) return null;
      var msg = __formatString("Unable to locate a 'Type' by the name: '%1'. Be sure to execute a query or call fetchMetadata first.", typeName);
      throw new Error(msg);

    }
    if (type.length) {
      var typeNames = type.join(",");
      throw new Error("There are multiple types with this 'shortName': " + typeNames);
    }
    return type;
  };

  /**
  Returns an array containing all of the  {{#crossLink "EntityType"}}{{/crossLink}}s or {{#crossLink "ComplexType"}}{{/crossLink}}s in this MetadataStore.
  @example
      // assume em1 is a preexisting EntityManager
      var allTypes = em1.metadataStore.getEntityTypes();
  @method getEntityTypes
  @return {Array of EntityType|ComplexType}
  **/
  proto.getEntityTypes = function () {
    return getTypesFromMap(this._structuralTypeMap);
  };

  proto.getIncompleteNavigationProperties = function () {
    return __objectMap(this._incompleteTypeMap, function (key, value) {
      return value;
    });
  };

  /**
  Returns a fully qualified entityTypeName for a specified resource name.  The reverse of this operation
  can be obtained via the  {{#crossLink "EntityType"}}{{/crossLink}} 'defaultResourceName' property
  @method getEntityTypeNameForResourceName
  @param resourceName {String}
  **/
  proto.getEntityTypeNameForResourceName = function (resourceName) {
    assertParam(resourceName, "resourceName").isString().check();
    return this._resourceEntityTypeMap[resourceName];
  };

  /**
  Associates a resourceName with an entityType.

  This method is only needed in those cases where multiple resources return the same
  entityType.  In this case Metadata discovery will only determine a single resource name for
  each entityType.
  @method setEntityTypeForResourceName
  @param resourceName {String}
  @param entityTypeOrName {EntityType|String} If passing a string either the fully qualified name or a short name may be used. If a short name is specified and multiple types share
  that same short name an exception will be thrown. If the entityType has not yet been discovered then a fully qualified name must be used.
  **/
  proto.setEntityTypeForResourceName = function (resourceName, entityTypeOrName) {
    assertParam(resourceName, "resourceName").isString().check();
    assertParam(entityTypeOrName, "entityTypeOrName").isInstanceOf(EntityType).or().isString().check();

    var entityTypeName;
    if (entityTypeOrName instanceof EntityType) {
      entityTypeName = entityTypeOrName.name;
    } else {
      entityTypeName = getQualifiedTypeName(this, entityTypeOrName, true);
    }

    this._resourceEntityTypeMap[resourceName] = entityTypeName;
    var entityType = this._getEntityType(entityTypeName, true);
    if (entityType && !entityType.defaultResourceName) {
      entityType.defaultResourceName = resourceName;
    }
  };


  // protected methods

  proto._checkEntityType = function (entity) {
    if (entity.entityType) return;
    var typeName = entity.prototype._$typeName;
    if (!typeName) {
      throw new Error("This entity has not been registered. See the MetadataStore.registerEntityTypeCtor method");
    }
    var entityType = this._getEntityType(typeName);
    if (entityType) {
      entity.entityType = entityType;
    }
  };

  function getTypesFromMap(typeMap) {
    var types = [];
    for (var key in typeMap) {
      var value = typeMap[key];
      // skip 'shortName' entries
      if (key === value.name) {
        types.push(typeMap[key]);
      }
    }
    return types;
  }

  function structuralTypeFromJson(metadataStore, json, allowMerge) {
    var typeName = qualifyTypeName(json.shortName, json.namespace);
    var stype = metadataStore._getEntityType(typeName, true);
    if (stype) {
      if (allowMerge) {
        return mergeStructuralType(stype, json);
      } else {
        // allow it but don't replace anything.
        return stype;
      }
    }
    var config = {
      shortName: json.shortName,
      namespace: json.namespace,
      isAbstract: json.isAbstract,
      autoGeneratedKeyType: AutoGeneratedKeyType.fromName(json.autoGeneratedKeyType),
      defaultResourceName: json.defaultResourceName,
      custom: json.custom
    };

    stype = json.isComplexType ? new ComplexType(config) : new EntityType(config);

    // baseType may not have been imported yet so we need to defer handling this type until later.
    if (json.baseTypeName) {
      stype.baseTypeName = json.baseTypeName;
      var baseEntityType = metadataStore._getEntityType(json.baseTypeName, true);
      if (baseEntityType) {
        completeStructuralTypeFromJson(metadataStore, json, stype, baseEntityType);
      } else {
        __getArray(metadataStore._deferredTypes, json.baseTypeName).push({ json: json, stype: stype });

      }
    } else {
      completeStructuralTypeFromJson(metadataStore, json, stype);
    }

    // stype may or may not have been added to the metadataStore at this point.
    return stype;
  }

  function mergeStructuralType(stype, json) {
    if (json.custom) {
      stype.custom = json.custom;
    }

    mergeProps(stype, json.dataProperties);
    mergeProps(stype, json.navigationProperties);
    return stype;
  }

  function mergeProps(stype, jsonProps) {
    if (!jsonProps) return;
    jsonProps.forEach(function (jsonProp) {
      var propName = jsonProp.name;
      if (!propName) {
        if (jsonProp.nameOnServer) {
          propName = stype.metadataStore.namingConvention.serverPropertyNameToClient(jsonProp.nameOnServer, {});
        } else {
          throw new Error("Unable to complete 'importMetadata' - cannot locate a 'name' or 'nameOnServer' for one of the imported property nodes");
        }
      }
      if (jsonProp.custom) {
        var prop = stype.getProperty(propName, true);
        prop.custom = jsonProp.custom;
      }
    });
  }

  function completeStructuralTypeFromJson(metadataStore, json, stype) {

    // validators from baseType work because validation walks thru base types
    // so no need to copy down.
    if (json.validators) {
      stype.validators = json.validators.map(Validator.fromJSON);
    }


    json.dataProperties.forEach(function (dp) {
      stype._addPropertyCore(DataProperty.fromJSON(dp));
    });


    var isEntityType = !json.isComplexType;
    if (isEntityType) {
      //noinspection JSHint
      json.navigationProperties && json.navigationProperties.forEach(function (np) {
        stype._addPropertyCore(NavigationProperty.fromJSON(np));
      });
    }

    metadataStore.addEntityType(stype);

    var deferredTypes = metadataStore._deferredTypes;
    var deferrals = deferredTypes[stype.name];
    if (deferrals) {
      deferrals.forEach(function (d) {
        completeStructuralTypeFromJson(metadataStore, d.json, d.stype);
      });
      delete deferredTypes[stype.name];
    }
  }

  function getQualifiedTypeName(metadataStore, structTypeName, throwIfNotFound) {
    if (isQualifiedTypeName(structTypeName)) return structTypeName;
    var result = metadataStore._shortNameMap[structTypeName];
    if (!result && throwIfNotFound) {
      throw new Error("Unable to locate 'entityTypeName' of: " + structTypeName);
    }
    return result;
  }

  return ctor;
})();

var CsdlMetadataParser = (function () {

  function parse(metadataStore, schemas, altMetadata) {

    metadataStore._entityTypeResourceMap = {};
    schemas = __toArray(schemas);
    schemas.forEach(function (schema) {
      if (schema.cSpaceOSpaceMapping) {
        // Web api only - not avail in OData.
        var mappings = JSON.parse(schema.cSpaceOSpaceMapping);
        var newMap = {};
        mappings.forEach(function (mapping) {
          newMap[mapping[0]] = mapping[1];
        });
        schema.cSpaceOSpaceMapping = newMap;
      }

      if (schema.entityContainer) {
        __toArray(schema.entityContainer).forEach(function (container) {
          __toArray(container.entitySet).forEach(function (entitySet) {
            var entityTypeName = parseTypeNameWithSchema(entitySet.entityType, schema).typeName;
            metadataStore.setEntityTypeForResourceName(entitySet.name, entityTypeName);
            metadataStore._entityTypeResourceMap[entityTypeName] = entitySet.name;
          });
        });
      }

      // process complextypes before entity types.
      if (schema.complexType) {
        __toArray(schema.complexType).forEach(function (ct) {
          var complexType = parseCsdlComplexType(ct, schema, metadataStore);
        });
      }
      if (schema.entityType) {
        __toArray(schema.entityType).forEach(function (et) {
          var entityType = parseCsdlEntityType(et, schema, schemas, metadataStore);

        });
      }

    });
    var badNavProps = metadataStore.getIncompleteNavigationProperties();
    if (badNavProps.length > 0) {
      var msg = badNavProps.map(function(npa) {
        if (Array.isArray(npa)) {
          return npa.map(function(np) {
            return np.parentType.name + ":" + np.name;
          }).join(', ');
        }
        return npa.parentType.name + ":" + npa.name;
      }).join(', ');
      throw new Error("Incomplete navigation properties: " + msg);
    }
    if (altMetadata) {
      metadataStore.importMetadata(altMetadata, true);
    }
    return metadataStore;
  }

  function parseCsdlEntityType(csdlEntityType, schema, schemas, metadataStore) {
    var shortName = csdlEntityType.name;
    var ns = getNamespaceFor(shortName, schema);
    var entityType = new EntityType({
      shortName: shortName,
      namespace: ns,
      isAbstract: csdlEntityType.abstract && csdlEntityType.abstract === 'true'
    });
    if (csdlEntityType.baseType) {
      var baseTypeName = parseTypeNameWithSchema(csdlEntityType.baseType, schema).typeName;
      entityType.baseTypeName = baseTypeName;
      var baseEntityType = metadataStore._getEntityType(baseTypeName, true);
      if (baseEntityType) {
        completeParseCsdlEntityType(entityType, csdlEntityType, schema, schemas, metadataStore);
      } else {
        var deferrals = metadataStore._deferredTypes[baseTypeName];
        if (!deferrals) {
          deferrals = [];
          metadataStore._deferredTypes[baseTypeName] = deferrals;
        }
        deferrals.push({ entityType: entityType, csdlEntityType: csdlEntityType });
      }
    } else {
      completeParseCsdlEntityType(entityType, csdlEntityType, schema, schemas, metadataStore);
    }
    // entityType may or may not have been added to the metadataStore at this point.
    return entityType;

  }

  function completeParseCsdlEntityType(entityType, csdlEntityType, schema, schemas, metadataStore) {
    var keyNamesOnServer = csdlEntityType.key ? __toArray(csdlEntityType.key.propertyRef).map(__pluck("name")) : [];

    __toArray(csdlEntityType.property).forEach(function (prop) {
      parseCsdlDataProperty(entityType, prop, schema, keyNamesOnServer);
    });

    __toArray(csdlEntityType.navigationProperty).forEach(function (prop) {
      parseCsdlNavProperty(entityType, prop, schema, schemas);
    });

    metadataStore.addEntityType(entityType);
    entityType.defaultResourceName = metadataStore._entityTypeResourceMap[entityType.name];

    var deferredTypes = metadataStore._deferredTypes;
    var deferrals = deferredTypes[entityType.name];
    if (deferrals) {
      deferrals.forEach(function (d) {
        completeParseCsdlEntityType(d.entityType, d.csdlEntityType, schema, schemas, metadataStore);
      });
      delete deferredTypes[entityType.name];
    }

  }

  function parseCsdlComplexType(csdlComplexType, schema, metadataStore) {
    var shortName = csdlComplexType.name;
    var ns = getNamespaceFor(shortName, schema);
    var complexType = new ComplexType({
      shortName: shortName,
      namespace: ns
    });

    __toArray(csdlComplexType.property).forEach(function (prop) {
      parseCsdlDataProperty(complexType, prop, schema);
    });

    metadataStore.addEntityType(complexType);
    return complexType;
  }

  function parseCsdlDataProperty(parentType, csdlProperty, schema, keyNamesOnServer) {
    var dp;
    var typeParts = csdlProperty.type.split(".");
    // Both tests on typeParts are necessary because of differing metadata conventions for OData and Edmx feeds.
    if (typeParts[0] === "Edm" && typeParts.length === 2) {
      dp = parseCsdlSimpleProperty(parentType, csdlProperty, keyNamesOnServer);
    } else {
      if (isEnumType(csdlProperty, schema)) {
        dp = parseCsdlSimpleProperty(parentType, csdlProperty, keyNamesOnServer);
        if (dp) {
          dp.enumType = csdlProperty.type;
        }
      } else {
        dp = parseCsdlComplexProperty(parentType, csdlProperty, schema);
      }
    }
    if (dp) {
      parentType._addPropertyCore(dp);
      addValidators(dp);
    }
    return dp;
  }

  function parseCsdlSimpleProperty(parentType, csdlProperty, keyNamesOnServer) {
    var dataType = DataType.fromEdmDataType(csdlProperty.type);
    if (dataType == null) {
      parentType.warnings.push("Unable to recognize DataType for property: " + csdlProperty.name + " DateType: " + csdlProperty.type);
      return null;
    }
    var isNullable = csdlProperty.nullable === 'true' || csdlProperty.nullable == null;
    // var fixedLength = csdlProperty.fixedLength ? csdlProperty.fixedLength === true : undefined;
    var isPartOfKey = keyNamesOnServer != null && keyNamesOnServer.indexOf(csdlProperty.name) >= 0;
    if (isPartOfKey && parentType.autoGeneratedKeyType === AutoGeneratedKeyType.None) {
      if (isIdentityProperty(csdlProperty)) {
        parentType.autoGeneratedKeyType = AutoGeneratedKeyType.Identity;
      }
    }
    // TODO: nit - don't set maxLength if null;
    var maxLength = csdlProperty.maxLength;
    maxLength = (maxLength == null || maxLength === "Max") ? null : parseInt(maxLength, 10);
    // can't set the name until we go thru namingConventions and these need the dp.


    var dp = new DataProperty({
      nameOnServer: csdlProperty.name,
      dataType: dataType,
      isNullable: isNullable,
      isPartOfKey: isPartOfKey,
      maxLength: maxLength,
      defaultValue: csdlProperty.defaultValue,
      // fixedLength: fixedLength,
      concurrencyMode: csdlProperty.concurrencyMode
    });

    if (dataType === DataType.Undefined) {
      dp.rawTypeName = csdlProperty.type;
    }
    return dp;
  }

  function parseCsdlComplexProperty(parentType, csdlProperty, schema) {

    // Complex properties are never nullable ( per EF specs)
    // var isNullable = csdlProperty.nullable === 'true' || csdlProperty.nullable == null;
    // var complexTypeName = csdlProperty.type.split("Edm.")[1];
    var complexTypeName = parseTypeNameWithSchema(csdlProperty.type, schema).typeName;
    // can't set the name until we go thru namingConventions and these need the dp.
    var dp = new DataProperty({
      nameOnServer: csdlProperty.name,
      complexTypeName: complexTypeName,
      isNullable: false
    });

    return dp;
  }

  function parseCsdlNavProperty(entityType, csdlProperty, schema, schemas) {
    var association = getAssociation(csdlProperty, schema, schemas);
    if (!association) {
      throw new Error("Unable to resolve Foreign Key Association: " + csdlProperty.relationship);
    }
    var toEnd = __arrayFirst(association.end, function (assocEnd) {
      return assocEnd.role === csdlProperty.toRole;
    });

    var isScalar = toEnd.multiplicity !== "*";
    var dataType = parseTypeNameWithSchema(toEnd.type, schema).typeName;

    var constraint = association.referentialConstraint;
    if (!constraint) {
      // TODO: Revisit this later - right now we just ignore many-many and assocs with missing constraints.

      // Think about adding this back later.
      if (association.end[0].multiplicity == "*" && association.end[1].multiplicity == "*") {
        // ignore many to many relations for now
        return;
      } else {
        // For now assume it will be set later directly on the client.
        // other alternative is to throw an error:
        // throw new Error("Foreign Key Associations must be turned on for this model");
      }
    }

    var cfg = {
      nameOnServer: csdlProperty.name,
      entityTypeName: dataType,
      isScalar: isScalar,
      associationName: association.name
    };

    if (constraint) {
      var principal = constraint.principal;
      var dependent = constraint.dependent;

      var propRefs = __toArray(dependent.propertyRef);
      var fkNames = propRefs.map(__pluck("name"));
      if (csdlProperty.fromRole === principal.role) {
        cfg.invForeignKeyNamesOnServer = fkNames;
      } else {
        // will be used later by np._update
        cfg.foreignKeyNamesOnServer = fkNames;
      }
    }

    var np = new NavigationProperty(cfg);
    entityType._addPropertyCore(np);
    return np;
  }

  function isEnumType(csdlProperty, schema) {
    if (schema.enumType) return isEdmxEnumType(csdlProperty, schema);
    else if (schema.extensions) return isODataEnumType(csdlProperty, schema);
    else return false;
  }

  function isEdmxEnumType(csdlProperty, schema) {
    var enumTypes = __toArray(schema.enumType);
    var typeParts = csdlProperty.type.split(".");
    var baseTypeName = typeParts[typeParts.length - 1];
    return enumTypes.some(function (enumType) {
      return enumType.name === baseTypeName;
    });
  }

  function isODataEnumType(csdlProperty, schema) {
    var enumTypes = schema.extensions.filter(function (ext) {
      return ext.name === "EnumType";
    });
    var typeParts = csdlProperty.type.split(".");
    var baseTypeName = typeParts[typeParts.length - 1];
    return enumTypes.some(function (enumType) {
      return enumType.attributes.some(function (attr) {
        return attr.name === "Name" && attr.value === baseTypeName;
      });
    });
  }

  function addValidators(dataProperty) {
    var typeValidator;
    if (!dataProperty.isNullable) {
      dataProperty.validators.push(Validator.required());
    }

    if (dataProperty.isComplexProperty) return;

    if (dataProperty.dataType === DataType.String) {
      if (dataProperty.maxLength) {
        var validatorArgs = { maxLength: dataProperty.maxLength };
        typeValidator = Validator.maxLength(validatorArgs);
      } else {
        typeValidator = Validator.string();
      }
    } else {
      typeValidator = dataProperty.dataType.validatorCtor();
    }

    dataProperty.validators.push(typeValidator);

  }

  function isIdentityProperty(csdlProperty) {
    // see if web api feed
    var propName = __arrayFirst(Object.keys(csdlProperty), function (pn) {
      return pn.indexOf("StoreGeneratedPattern") >= 0;
    });
    if (propName) {
      return (csdlProperty[propName] === "Identity");
    } else {
      // see if Odata feed
      var extensions = csdlProperty.extensions;
      if (!extensions) {
        return false;
      }
      var identityExtn = __arrayFirst(extensions, function (extension) {
        return extension.name === "StoreGeneratedPattern" && extension.value === "Identity";
      });
      return !!identityExtn;
    }
  }

  // Fast version
  // np: schema.entityType[].navigationProperty.relationship -> schema.association
  //   match( shortName(np.relationship) == schema.association[].name
  //      --> association__

  // Correct version
  // np: schema.entityType[].navigationProperty.relationship -> schema.association
  //   match( np.relationship == schema.entityContainer[0].associationSet[].association )
  //      -> associationSet.name
  //   match ( associationSet.name == schema.association[].name )
  //      -> association

  function getAssociation(csdlNavProperty, containingSchema, schemas) {
    var assocFullName = parseTypeNameWithSchema(csdlNavProperty.relationship, containingSchema);
    var assocNamespace = assocFullName.namespace;
    var assocSchema = __arrayFirst(schemas, function (schema) {
      return schema.namespace === assocNamespace;
    });
    if (!assocSchema) return null;
    
    var assocName = assocFullName.shortTypeName;
    var assocs = assocSchema.association;
    if (!assocs) return null;
    if (!Array.isArray(assocs)) {
      assocs = [assocs];
    }
    var association = __arrayFirst(assocs, function (assoc) {
      return assoc.name === assocName;
    });
    return association;
  }

  // schema is only needed for navProperty type name
  function parseTypeNameWithSchema(entityTypeName, schema) {
    var result = parseTypeName(entityTypeName);
    if (schema && schema.cSpaceOSpaceMapping) {
      var ns = getNamespaceFor(result.shortTypeName, schema);
      if (ns) {
        result = makeTypeHash(result.shortTypeName, ns);
      }
    }
    return result;
  }

  function getNamespaceFor(shortName, schema) {
    var ns;
    var mapping = schema.cSpaceOSpaceMapping;
    if (mapping) {
      var fullName = mapping[schema.namespace + "." + shortName];
      ns = fullName && fullName.substr(0, fullName.length - (shortName.length + 1));
      if (ns) return ns;
    }
    // if schema does not also have an entityType node then
    // this is an WebApi2 OData schema which is usually equal to 'Default'; which is useless.
    if (schema.entityType || schema.namespace != 'Default') {
      return schema.namespace;
    }
    return null;
  }

  return {
    parse: parse
  };

})();

var EntityType = (function () {
  /**
  Container for all of the metadata about a specific type of Entity.
  @class EntityType
  **/
  var __nextAnonIx = 0;


  /**
  @example
      var entityType = new EntityType( {
          shortName: "person",
          namespace: "myAppNamespace"
      });
  @method <ctor> EntityType
  @param config {Object|MetadataStore} Configuration settings or a MetadataStore.  If this parameter is just a MetadataStore
  then what will be created is an 'anonymous' type that will never be communicated to or from the server. It is purely for
  client side use and will be given an automatically generated name. Normally, however, you will use a configuration object.
  @param config.shortName {String}
  @param [config.namespace=""] {String}
  @param [config.baseTypeName] {String}
  @param [config.isAbstract=false] {Boolean}
  @param [config.autoGeneratedKeyType] {AutoGeneratedKeyType}
  @param [config.defaultResourceName] {String}
  @param [config.dataProperties] {Array of DataProperties}
  @param [config.navigationProperties] {Array of NavigationProperties}
  @param [config.serializerFn] A function that is used to mediate the serialization of instances of this type.
  @param [config.custom] {Object}
  **/
  var ctor = function EntityType(config) {
    if (arguments.length > 1) {
      throw new Error("The EntityType ctor has a single argument that is either a 'MetadataStore' or a configuration object.");
    }
    if (config._$typeName === "MetadataStore") {
      this.metadataStore = config;
      this.shortName = "Anon_" + (++__nextAnonIx);
      this.namespace = "";
      this.isAnonymous = true;
    } else {
      assertConfig(config)
          .whereParam("shortName").isNonEmptyString()
          .whereParam("namespace").isString().isOptional().withDefault("")
          .whereParam("baseTypeName").isString().isOptional()
          .whereParam("isAbstract").isBoolean().isOptional().withDefault(false)
          .whereParam("autoGeneratedKeyType").isEnumOf(AutoGeneratedKeyType).isOptional().withDefault(AutoGeneratedKeyType.None)
          .whereParam("defaultResourceName").isNonEmptyString().isOptional().withDefault(null)
          .whereParam("dataProperties").isOptional()
          .whereParam("navigationProperties").isOptional()
          .whereParam("serializerFn").isOptional().isFunction()
          .whereParam("custom").isOptional()
          .applyAll(this);
    }

    this.name = qualifyTypeName(this.shortName, this.namespace);

    // the defaultResourceName may also be set up either via metadata lookup or first query or via the 'setProperties' method
    this.dataProperties = [];
    this.navigationProperties = [];
    this.complexProperties = [];
    this.keyProperties = [];
    this.foreignKeyProperties = [];
    this.inverseForeignKeyProperties = [];
    this.concurrencyProperties = [];
    this.unmappedProperties = []; // will be updated later.
    this.validators = [];
    this.warnings = [];
    this._mappedPropertiesCount = 0;
    this.subtypes = [];
    // now process any data/nav props
    addProperties(this, config.dataProperties, DataProperty);
    addProperties(this, config.navigationProperties, NavigationProperty);
  };
  var proto = ctor.prototype;
  var parseRawValue = DataType.parseRawValue;
  proto._$typeName = "EntityType";
  ctor.qualifyTypeName = qualifyTypeName;

  /**
  The {{#crossLink "MetadataStore"}}{{/crossLink}} that contains this EntityType

  __readOnly__
  @property metadataStore {MetadataStore}
  **/

  /**
  The DataProperties (see {{#crossLink "DataProperty"}}{{/crossLink}}) associated with this EntityType.

  __readOnly__
  @property dataProperties {Array of DataProperty}
  **/

  /**
  The NavigationProperties  (see {{#crossLink "NavigationProperty"}}{{/crossLink}}) associated with this EntityType.

  __readOnly__
  @property navigationProperties {Array of NavigationProperty}
  **/

  /**
  The DataProperties for this EntityType that contain instances of a ComplexType (see {{#crossLink "ComplexType"}}{{/crossLink}}).

  __readOnly__
  @property complexProperties {Array of DataProperty}
  **/

  /**
  The DataProperties associated with this EntityType that make up it's {{#crossLink "EntityKey"}}{{/crossLink}}.

  __readOnly__
  @property keyProperties {Array of DataProperty}
  **/

  /**
  The DataProperties associated with this EntityType that are foreign key properties.

  __readOnly__
  @property foreignKeyProperties {Array of DataProperty}
  **/

  /**
  The DataProperties associated with this EntityType that are concurrency properties.

  __readOnly__
  @property concurrencyProperties {Array of DataProperty}
  **/

  /**
  The DataProperties associated with this EntityType that are not mapped to any backend datastore. These are effectively free standing
  properties.

  __readOnly__
  @property unmappedProperties {Array of DataProperty}
  **/

  /**
  The default resource name associated with this EntityType.  An EntityType may be queried via a variety of 'resource names' but this one
  is used as the default when no resource name is provided.  This will occur when calling {{#crossLink "EntityAspect/loadNavigationProperty"}}{{/crossLink}}
  or when executing any {{#crossLink "EntityQuery"}}{{/crossLink}} that was created via an {{#crossLink "EntityKey"}}{{/crossLink}}.

  __readOnly__
  @property defaultResourceName {String}
  **/

  /**
  The fully qualified name of this EntityType.

  __readOnly__
  @property name {String}
  **/

  /**
  The short, unqualified, name for this EntityType.

  __readOnly__
  @property shortName {String}
  **/

  /**
  The namespace for this EntityType.

  __readOnly__
  @property namespace {String}
  **/

  /**
  The base EntityType (if any) for this EntityType.

  __readOnly__
  @property baseEntityType {EntityType}
  **/

  /**
  Whether this EntityType is abstract.

  __readOnly__
  @property isAbstract {boolean}
  **/

  /**
  The {{#crossLink "AutoGeneratedKeyType"}}{{/crossLink}} for this EntityType.

  __readOnly__
  @property autoGeneratedKeyType {AutoGeneratedKeyType}
  @default AutoGeneratedKeyType.None
  **/

  /**
  The entity level validators associated with this EntityType. Validators can be added and
  removed from this collection.

  __readOnly__
  @property validators {Array of Validator}
  **/

  /**
  A free form object that can be used to define any custom metadata for this EntityType.

  __readOnly__
  @property custom {Object}
  **/

  /**
  General purpose property set method
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      custType.setProperties( {
          autoGeneratedKeyType: AutoGeneratedKeyType.Identity;
          defaultResourceName: "CustomersAndIncludedOrders"
      )};
  @method setProperties
  @param config [object]
  @param [config.autogeneratedKeyType] {AutoGeneratedKeyType}
  @param [config.defaultResourceName] {String}
  @param [config.serializerFn] A function that is used to mediate the serialization of instances of this type.
  @param [config.custom] {Object}
  **/
  proto.setProperties = function (config) {
    assertConfig(config)
        .whereParam("autoGeneratedKeyType").isEnumOf(AutoGeneratedKeyType).isOptional()
        .whereParam("defaultResourceName").isString().isOptional()
        .whereParam("serializerFn").isFunction().isOptional()
        .whereParam("custom").isOptional()
        .applyAll(this);
    if (config.defaultResourceName) {
      this.defaultResourceName = config.defaultResourceName;
    }
  };

  /**
  Returns whether this type is a subtype of a specified type.

  @method isSubtypeOf
  @param entityType [EntityType]
  **/
  proto.isSubtypeOf = function (entityType) {
    assertParam(entityType, "entityType").isInstanceOf(EntityType).check();
    var baseType = this;
    do {
      if (baseType === entityType) return true;
      baseType = baseType.baseEntityType;
    } while (baseType);
    return false;
  };

  /**
  Returns an array containing this type and any/all subtypes of this type down thru the hierarchy.

  @method getSelfAndSubtypes
  **/
  proto.getSelfAndSubtypes = function () {
    var result = [this];
    this.subtypes.forEach(function (st) {
      var subtypes = st.getSelfAndSubtypes();
      result.push.apply(result, subtypes);
    });
    return result;
  };

  proto.getAllValidators = function () {
    var result = this.validators.slice(0);
    var bt = this.baseEntityType;
    while (bt) {
      result.push.apply(result, bt.validators);
      bt = bt.baseEntityType;
    }
    ;
    return result;
  }

  /**
  Adds a  {{#crossLink "DataProperty"}}{{/crossLink}} or a {{#crossLink "NavigationProperty"}}{{/crossLink}} to this EntityType.
  @example
      // assume myEntityType is a newly constructed EntityType.
      myEntityType.addProperty(dataProperty1);
      myEntityType.addProperty(dataProperty2);
      myEntityType.addProperty(navigationProperty1);
  @method addProperty
  @param property {DataProperty|NavigationProperty}
  **/
  proto.addProperty = function (property) {
    assertParam(property, "property").isInstanceOf(DataProperty).or().isInstanceOf(NavigationProperty).check();

    // true is 2nd arg to force resolve of any navigation properties.
    var newprop = this._addPropertyCore(property, true);

    if (this.subtypes && this.subtypes.length) {
      var stype = this;
      stype.getSelfAndSubtypes().forEach(function (st) {
        if (st !== stype) {
          if (property.isNavigationProperty) {
            st._addPropertyCore(new NavigationProperty(property), true);
          } else {
            st._addPropertyCore(new DataProperty(property), true);
          }
        }
      });
    }
    return newprop;
  };

  proto._updateFromBase = function (baseEntityType) {
    this.baseEntityType = baseEntityType;
    if (this.autoGeneratedKeyType === AutoGeneratedKeyType.None) {
      this.autoGeneratedKeyType = baseEntityType.autoGeneratedKeyType;
    }

    baseEntityType.dataProperties.forEach(function (dp) {
      var newDp = new DataProperty(dp);
      // don't need to copy validators becaue we will walk the hierarchy to find them
      newDp.validators = [];
      newDp.baseProperty = dp;
      this._addPropertyCore(newDp);
    }, this);
    baseEntityType.navigationProperties.forEach(function (np) {
      var newNp = new NavigationProperty(np);
      // don't need to copy validators becaue we will walk the hierarchy to find them
      newNp.validators = [];
      newNp.baseProperty = np;
      this._addPropertyCore(newNp);
    }, this);
    baseEntityType.subtypes.push(this);
  }

  proto._addPropertyCore = function (property, shouldResolve) {
    if (this.isFrozen) {
      throw new Error("The '" + this.name + "' EntityType/ComplexType has been frozen. You can only add properties to an EntityType/ComplexType before any instances of that type have been created and attached to an entityManager.");
    }
    var parentType = property.parentType;
    if (parentType) {
      if (parentType !== this) {
        throw new Error("This property: " + property.name + " has already been added to " + property.parentType.name);
      } else {
        // adding the same property more than once to the same entityType is just ignored.
        return this;
      }
    }
    property.parentType = this;
    var ms = this.metadataStore;
    if (property.isDataProperty) {
      this._addDataProperty(property);
    } else {
      this._addNavigationProperty(property);
      // metadataStore can be undefined if this entityType has not yet been added to a MetadataStore.
      if (shouldResolve && ms) {
        tryResolveNp(property, ms);
      }
    }
    // unmapped properties can be added AFTER entityType has already resolved all property names.
    if (ms && !(property.name && property.nameOnServer)) {
      updateClientServerNames(ms.namingConvention, property, "name");
    }
    // props can be added after entity prototype has already been wrapped.
    if (ms && this._extra) {
      if (this._extra.alreadyWrappedProps) {
        var proto = this._ctor.prototype;
        __modelLibraryDef.getDefaultInstance().initializeEntityPrototype(proto);
      }
    }
    return this;
  };

  /**
  Create a new entity of this type.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var cust1 = custType.createEntity();
      em1.addEntity(cust1);
  @method createEntity
  @param [initialValues] {Config object} - Configuration object of the properties to set immediately after creation.
  @return {Entity} The new entity.
  **/
  proto.createEntity = function (initialValues) {
    // ignore the _$eref once the entity is attached to an entityManager.
    if (initialValues && initialValues._$eref && !initialValues._$eref.entityAspect.entityManager) return initialValues._$eref;

    var instance = this._createInstanceCore();

    if (initialValues) {
      // only assign an _eref if the object is fully "keyed"
      if (this.keyProperties.every(function (kp) {
        return initialValues[kp.name] != null;
      })) {
        initialValues._$eref = instance;
      }
      ;

      this._updateTargetFromRaw(instance, initialValues, getRawValueFromConfig);

      this.navigationProperties.forEach(function (np) {
        var relatedEntity;
        var val = initialValues[np.name];
        if (val != undefined) {
          var navEntityType = np.entityType;
          if (np.isScalar) {
            relatedEntity = val.entityAspect ? val : navEntityType.createEntity(val);
            instance.setProperty(np.name, relatedEntity);
          } else {
            var relatedEntities = instance.getProperty(np.name);
            val.forEach(function (v) {
              relatedEntity = v.entityAspect ? v : navEntityType.createEntity(v);
              relatedEntities.push(relatedEntity);
            });
          }
        }
      });
    }

    this._initializeInstance(instance);
    return instance;
  };

  function getRawValueFromConfig(rawEntity, dp) {
    // 'true' fork can happen if an initializer contains an actaul instance of an already created complex object.
    return (rawEntity.entityAspect || rawEntity.complexAspect) ? rawEntity.getProperty(dp.name) : rawEntity[dp.name];
  }

  proto._createInstanceCore = function () {
    var aCtor = this.getEntityCtor();
    var instance = new aCtor();
    new EntityAspect(instance);
    return instance;
  };

  proto._initializeInstance = function (instance) {
    if (this.baseEntityType) {
      this.baseEntityType._initializeInstance(instance);
    }
    var initFn = this.initFn;
    if (initFn) {
      if (typeof initFn === "string") {
        initFn = instance[initFn];
      }
      initFn(instance);
    }
    this.complexProperties && this.complexProperties.forEach(function (cp) {
      var ctInstance = instance.getProperty(cp.name);
      if (Array.isArray(ctInstance)) {
        ctInstance.forEach(function (ctInst) {
          cp.dataType._initializeInstance(ctInst);
        });
      } else {
        cp.dataType._initializeInstance(ctInstance);
      }
    });
    // not needed for complexObjects
    if (instance.entityAspect) {
      instance.entityAspect._initialized = true;
    }
  };

  /**
  Returns the constructor for this EntityType.
  @method getCtor ( or obsolete getEntityCtor)
  @return {Function} The constructor for this EntityType.
  **/
  proto.getCtor = proto.getEntityCtor = function (forceRefresh) {
    if (this._ctor && !forceRefresh) return this._ctor;

    var ctorRegistry = this.metadataStore._ctorRegistry;
    var r = ctorRegistry[this.name] || ctorRegistry[this.shortName] || {};
    var aCtor = r.ctor || this._ctor;

    var ctorType = aCtor && aCtor.prototype && (aCtor.prototype.entityType || aCtor.prototype.complexType);
    if (ctorType && ctorType.metadataStore !== this.metadataStore) {
      // We can't risk a mismatch between the ctor and the type info in a specific metadatastore
      // because modelLibraries rely on type info to intercept ctor properties
      throw new Error("Cannot register the same constructor for " + this.name + " in different metadata stores.  Please define a separate constructor for each metadata store.");
    }


    if (r.ctor && forceRefresh) {
      this._extra = undefined;
    }

    if (!aCtor) {
      var createCtor = __modelLibraryDef.getDefaultInstance().createCtor;
      aCtor = createCtor ? createCtor(this) : createEmptyCtor(this);
    }

    this.initFn = r.initFn;
    this.noTrackingFn = r.noTrackingFn;

    aCtor.prototype._$typeName = this.name;
    this._setCtor(aCtor);
    return aCtor;
  };

  function createEmptyCtor(type) {
    var name = type.name.replace(/\W/g, '_');
    return Function('return function '+name+'(){}')();
  }

  // May make public later.
  proto._setCtor = function (aCtor, interceptor) {

    var instanceProto = aCtor.prototype;

    // place for extra breeze related data
    this._extra = this._extra || {};

    var instance = new aCtor();
    calcUnmappedProperties(this, instance);

    if (this._$typeName === "EntityType") {
      // insure that all of the properties are on the 'template' instance before watching the class.
      instanceProto.entityType = this;
    } else {
      instanceProto.complexType = this;
    }

    // defaultPropertyInterceptor is a 'global' (but internal to breeze) function;
    instanceProto._$interceptor = interceptor || defaultPropertyInterceptor;
    __modelLibraryDef.getDefaultInstance().initializeEntityPrototype(instanceProto);
    this._ctor = aCtor;
  };

  /**
  Adds either an entity or property level validator to this EntityType.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var countryProp = custType.getProperty("Country");
      var valFn = function (v) {
              if (v == null) return true;
              return (core.stringStartsWith(v, "US"));
          };
      var countryValidator = new Validator("countryIsUS", valFn,
      { displayName: "Country", messageTemplate: "'%displayName%' must start with 'US'" });
      custType.addValidator(countryValidator, countryProp);
  This is the same as adding an entity level validator via the 'validators' property of DataProperty or NavigationProperty
  @example
      countryProp.validators.push(countryValidator);
  Entity level validators can also be added by omitting the 'property' parameter.
  @example
      custType.addValidator(someEntityLevelValidator);
  or
  @example
      custType.validators.push(someEntityLevelValidator);
  @method addValidator
  @param validator {Validator} Validator to add.
  @param [property] Property to add this validator to.  If omitted, the validator is assumed to be an
  entity level validator and is added to the EntityType's 'validators'.
  **/
  proto.addValidator = function (validator, property) {
    assertParam(validator, "validator").isInstanceOf(Validator).check();
    assertParam(property, "property").isOptional().isString().or().isEntityProperty().check();
    if (property) {
      if (typeof (property) === 'string') {
        property = this.getProperty(property, true);
      }
      property.validators.push(validator);
    } else {
      this.validators.push(validator);
    }
  };

  /**
  Returns all of the properties ( dataProperties and navigationProperties) for this EntityType.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var arrayOfProps = custType.getProperties();
  @method getProperties
  @return {Array of DataProperty|NavigationProperty} Array of Data and Navigation properties.
  **/
  proto.getProperties = function () {
    return this.dataProperties.concat(this.navigationProperties);
  };

  /**
  Returns all of the property names ( for both dataProperties and navigationProperties) for this EntityType.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var arrayOfPropNames = custType.getPropertyNames();
  @method getPropertyNames
  @return {Array of String}
  **/
  proto.getPropertyNames = function () {
    return this.getProperties().map(__pluck('name'));
  };

  /**
  Returns a data property with the specified name or null.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var customerNameDataProp = custType.getDataProperty("CustomerName");
  @method getDataProperty
  @param propertyName {String}
  @return {DataProperty} Will be null if not found.
  **/
  proto.getDataProperty = function (propertyName) {
    return __arrayFirst(this.dataProperties, __propEq('name', propertyName));
  };

  /**
  Returns a navigation property with the specified name or null.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var customerOrdersNavProp = custType.getDataProperty("Orders");
  @method getNavigationProperty
  @param propertyName {String}
  @return {NavigationProperty} Will be null if not found.
  **/
  proto.getNavigationProperty = function (propertyName) {
    return __arrayFirst(this.navigationProperties, __propEq('name', propertyName));
  };

  /**
  Returns either a DataProperty or a NavigationProperty with the specified name or null.

  This method also accepts a '.' delimited property path and will return the 'property' at the
  end of the path.
  @example
      var custType = em1.metadataStore.getEntityType("Customer");
      var companyNameProp = custType.getProperty("CompanyName");
  This method can also walk a property path to return a property
  @example
      var orderDetailType = em1.metadataStore.getEntityType("OrderDetail");
      var companyNameProp2 = orderDetailType.getProperty("Order.Customer.CompanyName");
      // companyNameProp === companyNameProp2
  @method getProperty
  @param propertyPath {String}
  @param [throwIfNotFound=false] {Boolean} Whether to throw an exception if not found.
  @return {DataProperty|NavigationProperty} Will be null if not found.
  **/
  proto.getProperty = function (propertyPath, throwIfNotFound) {
    var props = this.getPropertiesOnPath(propertyPath, false, throwIfNotFound);
    return props ? props[props.length - 1] : null;
  };

  proto.getPropertiesOnPath = function(propertyPath, useServerName, throwIfNotFound) {
    throwIfNotFound = throwIfNotFound || false;
    var propertyNames = (Array.isArray(propertyPath)) ? propertyPath : propertyPath.trim().split('.');

    var ok = true;
    var parentType = this;
    var key = useServerName ? "nameOnServer" : "name";
    var props = propertyNames.map(function (propName) {
      var prop = __arrayFirst(parentType.getProperties(), __propEq(key, propName));
      if (prop) {
        parentType = prop.isNavigationProperty ? prop.entityType : prop.dataType;
      } else if (throwIfNotFound) {
        throw new Error("unable to locate property: " + propName + " on entityType: " + parentType.name);
      } else {
        ok = false;
      }
      return prop;
    });
    return ok ? props : null;
  }

  proto.clientPropertyPathToServer = function(propertyPath, delimiter) {
    var delimiter = delimiter || '.';
    var propNames;
    if (this.isAnonymous) {
      var fn = this.metadataStore.namingConvention.clientPropertyNameToServer;
      propNames = propertyPath.split(".").map(function (propName) {
        return fn(propName);
      });
    } else {
      propNames = this.getPropertiesOnPath(propertyPath, false, true).map(function(prop) {
        return prop.nameOnServer;
      });
    }
    return propNames.join(delimiter);
  }

  proto.getEntityKeyFromRawEntity = function (rawEntity, rawValueFn) {
    var keyValues = this.keyProperties.map(function (dp) {
      var val = rawValueFn(rawEntity, dp);
      return parseRawValue(val, dp.dataType);
    });
    return new EntityKey(this, keyValues);
  };

  proto._updateTargetFromRaw = function (target, raw, rawValueFn) {
    // called recursively for complex properties
    this.dataProperties.forEach(function (dp) {
      if (!dp.isSettable) return;
      var rawVal = rawValueFn(raw, dp);
      if (rawVal === undefined) return;
      var dataType = dp.dataType; // this will be a complexType when dp is a complexProperty
      var oldVal;
      if (dp.isComplexProperty) {
        if (rawVal === null) return; // rawVal may be null in nosql dbs where it was never defined for the given row.
        oldVal = target.getProperty(dp.name);
        if (dp.isScalar) {
          dataType._updateTargetFromRaw(oldVal, rawVal, rawValueFn);
        } else {
          if (Array.isArray(rawVal)) {
            var newVal = rawVal.map(function (rawCo) {
              var newCo = dataType._createInstanceCore(target, dp);
              dataType._updateTargetFromRaw(newCo, rawCo, rawValueFn);
              dataType._initializeInstance(newCo);
              return newCo;
            });
            if (!__arrayEquals(oldVal, newVal, coEquals)) {
              // clear the old array and push new objects into it.
              oldVal.length = 0;
              newVal.forEach(function (nv) {
                oldVal.push(nv);
              });
            }
          } else {
            oldVal.length = 0;
          }
        }
      } else {
        var val;
        if (dp.isScalar) {
          var newVal = parseRawValue(rawVal, dataType);
          target.setProperty(dp.name, newVal);
        } else {
          oldVal = target.getProperty(dp.name);
          if (Array.isArray(rawVal)) {
            // need to compare values
            var newVal = rawVal.map(function (rv) {
              return parseRawValue(rv, dataType);
            });
            if (!__arrayEquals(oldVal, newVal)) {
              // clear the old array and push new objects into it.
              oldVal.length = 0;
              newVal.forEach(function (nv) {
                oldVal.push(nv);
              });
            }
          } else {
            oldVal.length = 0;
          }

        }
      }
    });

    // if merging from an import then raw will have an entityAspect or a complexAspect
    var rawAspect = raw.entityAspect || raw.complexAspect;
    if (rawAspect) {
      var targetAspect = target.entityAspect || target.complexAspect;
      if (rawAspect.originalValuesMap) {
        targetAspect.originalValues = rawAspect.originalValuesMap;
      }
      if (rawAspect.extraMetadata) {
        targetAspect.extraMetadata = rawAspect.extraMetadata;
      }
    }
  }

  function coEquals(co1, co2) {
    var dataProps = co1.complexAspect.parentProperty.dataType.dataProperties;
    var areEqual = dataProps.every(function (dp) {
      if (!dp.isSettable) return true;
      var v1 = co1.getProperty(dp.name);
      var v2 = co2.getProperty(dp.name);
      if (dp.isComplexProperty && dp.isScalar) {
        return coEquals(v1, v2);
      }
      else if(dp.isComplexProperty && !dp.isScalar) {
        return __arrayEquals(v1, v2, coEquals)
      } else {
        var dataType = dp.dataType; // this will be a complexType when dp is a complexProperty
        return (v1 === v2 || (dataType && dataType.normalize && v1 && v2 && dataType.normalize(v1) === dataType.normalize(v2)));
      }
    });
    return areEqual;
  }

  /**
  Returns a string representation of this EntityType.
  @method toString
  @return {String}
  **/
  proto.toString = function () {
    return this.name;
  };

  proto.toJSON = function () {
    return __toJson(this, {
      shortName: null,
      namespace: null,
      baseTypeName: null,
      isAbstract: false,
      autoGeneratedKeyType: null, // do not suppress default value
      defaultResourceName: null,
      dataProperties: localPropsOnly,
      navigationProperties: localPropsOnly,
      validators: null,
      custom: null
    });
  };

  function localPropsOnly(props) {
    return props.filter(function (prop) {
      return prop.baseProperty == null;
    });
  }



  proto._updateNames = function (property) {
    var nc = this.metadataStore.namingConvention;
    updateClientServerNames(nc, property, "name");

    if (property.isNavigationProperty) {
      updateClientServerNames(nc, property, "foreignKeyNames");
      updateClientServerNames(nc, property, "invForeignKeyNames");

      // these will get set later via _updateNps
      // this.inverse
      // this.entityType
      // this.relatedDataProperties
      //    dataProperty.relatedNavigationProperty
      //    dataProperty.inverseNavigationProperty
    }
  };



  function updateClientServerNames(nc, parent, clientPropName) {
    var serverPropName = clientPropName + "OnServer";
    var clientName = parent[clientPropName];
    if (clientName && clientName.length) {
      // if (parent.isUnmapped) return;
      var serverNames = __toArray(clientName).map(function (cName) {
        var sName = nc.clientPropertyNameToServer(cName, parent);
        var testName = nc.serverPropertyNameToClient(sName, parent);
        if (cName !== testName) {
          throw new Error("NamingConvention for this client property name does not roundtrip properly:" + cName + "-->" + testName);
        }
        return sName;
      });
      parent[serverPropName] = Array.isArray(clientName) ? serverNames : serverNames[0];
    } else {
      var serverName = parent[serverPropName];
      if ((!serverName) || serverName.length === 0) return;
      var clientNames = __toArray(serverName).map(function (sName) {
        var cName = nc.serverPropertyNameToClient(sName, parent);
        var testName = nc.clientPropertyNameToServer(cName, parent);
        if (sName !== testName) {
          throw new Error("NamingConvention for this server property name does not roundtrip properly:" + sName + "-->" + testName);
        }
        return cName;
      });
      parent[clientPropName] = Array.isArray(serverName) ? clientNames : clientNames[0];
    }
  }

  proto._checkNavProperty = function (navigationProperty) {
    if (navigationProperty.isNavigationProperty) {
      if (navigationProperty.parentType !== this) {
        throw new Error(__formatString("The navigationProperty '%1' is not a property of entity type '%2'",
            navigationProperty.name, this.name));
      }
      return navigationProperty;
    }

    if (typeof (navigationProperty) === 'string') {
      var np = this.getProperty(navigationProperty);
      if (np && np.isNavigationProperty) return np;
    }
    throw new Error("The 'navigationProperty' parameter must either be a NavigationProperty or the name of a NavigationProperty");
  };

  proto._addDataProperty = function (dp) {

    this.dataProperties.push(dp);

    if (dp.isPartOfKey) {
      this.keyProperties.push(dp);
    }

    if (dp.isComplexProperty) {
      this.complexProperties.push(dp);
    }

    if (dp.concurrencyMode && dp.concurrencyMode !== "None") {
      this.concurrencyProperties.push(dp);
    }

    if (dp.isUnmapped) {
      this.unmappedProperties.push(dp);
    }

  };

  proto._addNavigationProperty = function (np) {

    this.navigationProperties.push(np);

    if (!isQualifiedTypeName(np.entityTypeName)) {
      np.entityTypeName = qualifyTypeName(np.entityTypeName, this.namespace);
    }
  };

  proto._updateCps = function () {
    var metadataStore = this.metadataStore;
    var incompleteTypeMap = metadataStore._incompleteComplexTypeMap;
    this.complexProperties.forEach(function (cp) {
      if (cp.complexType) return;
      if (!resolveCp(cp, metadataStore)) {
        __getArray(incompleteTypeMap, cp.complexTypeName).push(cp);
      }
    });

    if (this.isComplexType) {
      (incompleteTypeMap[this.name] || []).forEach(function (cp) {
        resolveCp(cp, metadataStore);
      });
      delete incompleteTypeMap[this.name];
    }
  };

  function resolveCp(cp, metadataStore) {
    var complexType = metadataStore._getEntityType(cp.complexTypeName, true);
    if (!complexType) return false;
    if (!(complexType instanceof ComplexType)) {
      throw new Error("Unable to resolve ComplexType with the name: " + cp.complexTypeName + " for the property: " + property.name);
    }
    cp.dataType = complexType;
    cp.defaultValue = null;
    return true;
  }

  proto._updateNps = function () {
    var metadataStore = this.metadataStore;

    // resolve all navProps for this entityType
    this.navigationProperties.forEach(function (np) {
      tryResolveNp(np, metadataStore);
    });
    var incompleteTypeMap = metadataStore._incompleteTypeMap;
    // next resolve all navProp that point to this entityType.
    (incompleteTypeMap[this.name] || []).forEach(function (np) {
      tryResolveNp(np, metadataStore);
    });
    // every navProp that pointed to this type should now be resolved
    delete incompleteTypeMap[this.name];
  };

  function tryResolveNp(np, metadataStore) {
    if (np.entityType) return true;

    var entityType = metadataStore._getEntityType(np.entityTypeName, true);
    if (entityType) {
      np.entityType = entityType;
      np._resolveNp();
      // don't bother removing - _updateNps will do it later.
      // __arrayRemoveItem(incompleteNps, np, false);
    } else {
      var incompleteNps = __getArray(metadataStore._incompleteTypeMap, np.entityTypeName);
      __arrayAddItemUnique(incompleteNps, np);
    }
    return !!entityType;
  }

  function calcUnmappedProperties(stype, instance) {
    var metadataPropNames = stype.getPropertyNames();
    var modelLib = __modelLibraryDef.getDefaultInstance();
    var trackablePropNames = modelLib.getTrackablePropertyNames(instance);
    trackablePropNames.forEach(function (pn) {
      if (metadataPropNames.indexOf(pn) === -1) {
        var val = instance[pn];
        try {
          if (typeof val == "function") val = val();
        } catch (e) {
        }
        var dt = DataType.fromValue(val);
        var newProp = new DataProperty({
          name: pn,
          dataType: dt,
          isNullable: true,
          isUnmapped: true
        });
        newProp.isSettable = __isSettable(instance, pn);
        if (stype.subtypes && stype.subtypes.length) {
          stype.getSelfAndSubtypes().forEach(function (st) {
            st._addPropertyCore(new DataProperty(newProp));
          });
        } else {
          stype._addPropertyCore(newProp);
        }
      }
    });
  }

  return ctor;
})();

var ComplexType = (function () {
  /**
  Container for all of the metadata about a specific type of Complex object.
  @class ComplexType
  **/

  /**
  @example
      var complexType = new ComplexType( {
          shortName: "address",
          namespace: "myAppNamespace"
      });
  @method <ctor> ComplexType
  @param config {Object} Configuration settings
  @param config.shortName {String}
  @param [config.namespace=""] {String}
  @param [config.dataProperties] {Array of DataProperties}
  @param [config.custom] {Object}
  **/
  var ctor = function ComplexType(config) {
    if (arguments.length > 1) {
      throw new Error("The ComplexType ctor has a single argument that is a configuration object.");
    }

    assertConfig(config)
        .whereParam("shortName").isNonEmptyString()
        .whereParam("namespace").isString().isOptional().withDefault("")
        .whereParam("dataProperties").isOptional()
        .whereParam("isComplexType").isOptional().isBoolean()   // needed because this ctor can get called from the addEntityType method which needs the isComplexType prop
        .whereParam("custom").isOptional()
        .applyAll(this);

    this.name = qualifyTypeName(this.shortName, this.namespace);
    this.isComplexType = true;
    this.dataProperties = [];
    this.complexProperties = [];
    this.validators = [];
    this.concurrencyProperties = [];
    this.unmappedProperties = [];
    this.navigationProperties = []; // not yet supported
    this.keyProperties = []; // may be used later to enforce uniqueness on arrays of complextypes.

    addProperties(this, config.dataProperties, DataProperty);
  };
  var proto = ctor.prototype;
  /**
  The DataProperties (see {{#crossLink "DataProperty"}}{{/crossLink}}) associated with this ComplexType.

  __readOnly__
  @property dataProperties {Array of DataProperty}
  **/

  /**
  The DataProperties for this ComplexType that contain instances of a ComplexType (see {{#crossLink "ComplexType"}}{{/crossLink}}).

  __readOnly__
  @property complexProperties {Array of DataProperty}
  **/

  /**
  The DataProperties associated with this ComplexType that are not mapped to any backend datastore. These are effectively free standing
  properties.

  __readOnly__
  @property unmappedProperties {Array of DataProperty}
  **/

  /**
  The fully qualifed name of this ComplexType.

  __readOnly__
  @property name {String}
  **/

  /**
  The short, unqualified, name for this ComplexType.

  __readOnly__
  @property shortName {String}
  **/

  /**
  The namespace for this ComplexType.

  __readOnly__
  @property namespace {String}
  **/

  /**
  The entity level validators associated with this ComplexType. Validators can be added and
  removed from this collection.

  __readOnly__
  @property validators {Array of Validator}
  **/

  /**
  A free form object that can be used to define any custom metadata for this ComplexType.

  __readOnly__
  @property custom {Object}
  **/

  /**
  General purpose property set method
  @example
      // assume em1 is an EntityManager
      var addresstType = em1.metadataStore.getEntityType("Address");
      addressType.setProperties( {
          custom: { foo: 7, bar: "test" }
      });
  @method setProperties
  @param config [object]
  @param [config.custom] {Object}
  **/
  proto.setProperties = function (config) {
    assertConfig(config)
        .whereParam("custom").isOptional()
        .applyAll(this);
  };

  proto.getAllValidators = function () {
    // ComplexType inheritance is not YET supported.
    return this.validators;
  }

  /**
  Creates a new non-attached instance of this ComplexType.
  @method createInstance
  @param initialValues {Object} Configuration object containing initial values for the instance.
  **/
  // This method is actually the EntityType.createEntity method renamed 
  proto._createInstanceCore = function (parent, parentProperty) {
    var aCtor = this.getCtor();
    var instance = new aCtor();
    new ComplexAspect(instance, parent, parentProperty);
    // initialization occurs during either attach or in createInstance call.
    return instance;
  };


  proto.addProperty = function (dataProperty) {
    assertParam(dataProperty, "dataProperty").isInstanceOf(DataProperty).check();
    return this._addPropertyCore(dataProperty);
  };

  proto.getProperties = function () {
    return this.dataProperties;
  };

  /**
  See  {{#crossLink "EntityType.addValidator"}}{{/crossLink}}
  @method addValidator
  @param validator {Validator} Validator to add.
  @param [property] Property to add this validator to.  If omitted, the validator is assumed to be an
  entity level validator and is added to the EntityType's 'validators'.
  **/

  /**
  See  {{#crossLink "EntityType.getProperty"}}{{/crossLink}}
  @method getProperty
  **/

  /**
  See  {{#crossLink "EntityType.getPropertyNames"}}{{/crossLink}}
  @method getPropertyNames
  **/

  /**
  See  {{#crossLink "EntityType.getEntityCtor"}}{{/crossLink}}
  @method getCtor
  **/

  // copy entityType methods onto complexType
  proto = __extend(proto, EntityType.prototype, [
    "addValidator",
    "getProperty",
    "getPropertiesOnPath",
    "getPropertyNames",
    "_addPropertyCore",
    "_addDataProperty",
    "_updateNames",
    "_updateCps",
    "_initializeInstance",
    "_updateTargetFromRaw",
    "_setCtor"
  ]);

  // note the name change.
  proto.createInstance = EntityType.prototype.createEntity;  // name change
  proto.getCtor = EntityType.prototype.getEntityCtor;


  proto.toJSON = function () {
    return __toJson(this, {
      shortName: null,
      namespace: null,
      isComplexType: null,
      dataProperties: null,
      validators: null,
      custom: null
    });
  };


  proto._$typeName = "ComplexType";

  return ctor;
})();

var DataProperty = (function () {

  /**
  A DataProperty describes the metadata for a single property of an  {{#crossLink "EntityType"}}{{/crossLink}} that contains simple data.

  Instances of the DataProperty class are constructed automatically during Metadata retrieval. However it is also possible to construct them
  directly via the constructor.
  @class DataProperty
  **/

  /**
  @example
      var lastNameProp = new DataProperty( {
          name: "lastName",
          dataType: DataType.String,
          isNullable: true,
          maxLength: 20
      });
      // assuming personEntityType is a newly constructed EntityType
      personEntityType.addProperty(lastNameProperty);
  @method <ctor> DataProperty
  @param config {configuration Object}
  @param [config.name] {String}  The name of this property.
  @param [config.nameOnServer] {String} Same as above but the name is that defined on the server.
  Either this or the 'name' above must be specified. Whichever one is specified the other will be computed using
  the NamingConvention on the MetadataStore associated with the EntityType to which this will be added.
  @param [config.dataType=DataType.String] {DataType}
  @param [config.complexTypeName] {String}
  @param [config.isNullable=true] {Boolean}
  @param [config.isScalar=true] {Boolean}
  @param [config.defaultValue] {Any}
  @param [config.isPartOfKey=false] {Boolean}
  @param [config.isUnmapped=false] {Boolean}
  @param [config.concurrencyMode] {String}
  @param [config.maxLength] {Integer} Only meaningfull for DataType.String
  @param [config.validators] {Array of Validator}
  @param [config.custom] {Object}
  **/
  var ctor = function DataProperty(config) {
    assertConfig(config)
        .whereParam("name").isString().isOptional()
        .whereParam("nameOnServer").isString().isOptional()
        .whereParam("dataType").isEnumOf(DataType).isOptional().or().isString().or().isInstanceOf(ComplexType)
        .whereParam("complexTypeName").isOptional()
        .whereParam("isNullable").isBoolean().isOptional().withDefault(true)
        .whereParam("isScalar").isOptional().withDefault(true)// will be false for some NoSQL databases.
        .whereParam("defaultValue").isOptional()
        .whereParam("isPartOfKey").isBoolean().isOptional()
        .whereParam("isUnmapped").isBoolean().isOptional()
        .whereParam("isSettable").isBoolean().isOptional().withDefault(true)
        .whereParam("concurrencyMode").isString().isOptional()
        .whereParam("maxLength").isNumber().isOptional()
        .whereParam("validators").isInstanceOf(Validator).isArray().isOptional().withDefault([])
        .whereParam("displayName").isOptional()
        .whereParam("enumType").isOptional()
        .whereParam("rawTypeName").isOptional() // occurs with undefined datatypes
        .whereParam("custom").isOptional()
        .applyAll(this);
    var hasName = !!(this.name || this.nameOnServer);
    if (!hasName) {
      throw new Error("A DataProperty must be instantiated with either a 'name' or a 'nameOnServer' property");
    }
    // name/nameOnServer is resolved later when a metadataStore is available.

    if (this.complexTypeName) {
      this.isComplexProperty = true;
      this.dataType = null;
    } else if (typeof(this.dataType) === "string") {
      var dt = DataType.fromName(this.dataType);
      if (!dt) {
        throw new Error("Unable to find a DataType enumeration by the name of: " + this.dataType);
      }
      this.dataType = dt;
    } else if (!this.dataType) {
      this.dataType = DataType.String;
    }

    // == as opposed to === is deliberate here.
    if (this.defaultValue == null) {
      if (this.isNullable) {
        this.defaultValue = null;
      } else {
        if (this.isComplexProperty) {
          // what to do? - shouldn't happen from EF - but otherwise ???
        } else if (this.dataType === DataType.Binary) {
          this.defaultValue = "AAAAAAAAJ3U="; // hack for all binary fields but value is specifically valid for timestamp fields - arbitrary valid 8 byte base64 value.
        } else {
          this.defaultValue = this.dataType.defaultValue;
          if (this.defaultValue == null) {
            throw new Error("A nonnullable DataProperty cannot have a null defaultValue. Name: " + (this.name || this.nameOnServer));
          }
        }
      }
    } else if (this.dataType.isNumeric) {
      // in case the defaultValue comes in as a string ( which it does in EF6).
      if (typeof (this.defaultValue) === "string") {
        this.defaultValue = parseFloat(this.defaultValue);
      }
    }

    if (this.isComplexProperty) {
      this.isScalar = this.isScalar == null || this.isScalar === true;
    }

  };
  var proto = ctor.prototype;
  proto._$typeName = "DataProperty";

  ctor.getRawValueFromServer = function (rawEntity, dp) {
    if (dp.isUnmapped) {
      return rawEntity[dp.nameOnServer || dp.name];
    } else {
      var val = rawEntity[dp.nameOnServer];
      return val !== undefined ? val : dp.defaultValue;
    }
  }

  ctor.getRawValueFromClient = function (rawEntity, dp) {
    var val = rawEntity[dp.name];
    return val !== undefined ? val : dp.defaultValue;
  }


  /**
  The name of this property

  __readOnly__
  @property name {String}
  **/

  /**
  The display name of this property

  __readOnly__
  @property displayName {String} 
  **/
  
  /**
  The name of this property on the server

  __readOnly__
  @property nameOnServer {String} 
  **/
  
  /**
  The parent type that this property belongs to - will be either a {{#crossLink "EntityType"}}{{/crossLink}} or a {{#crossLink "ComplexType"}}{{/crossLink}}.

  __readOnly__
  @property parentType {EntityType|ComplexType}
  **/

  /**
  The {{#crossLink "DataType"}}{{/crossLink}} of this property.

  __readOnly__
  @property dataType {DataType}
  **/

  /**
  The name of the {{#crossLink "ComplexType"}}{{/crossLink}} associated with this property; may be null.

  __readOnly__
  @property complexTypeName {String}
  **/

  /**
  Whether the contents of this property is an instance of a {{#crossLink "ComplexType"}}{{/crossLink}}.

  __readOnly__
  @property isComplexProperty {bool}
  **/

  /**
  Whether this property is nullable.

  __readOnly__
  @property isNullable {Boolean}
  **/

  /**
  Whether this property is scalar (i.e., returns a single value).

  __readOnly__
  @property isScalar {Boolean}
  **/

  /**
  Property on the base type that this property is inherited from. Will be null if the property is not on the base type.

  __readOnly__
  @property baseProperty {DataProperty}
  **/

  /**
  Whether this property is a 'key' property.

  __readOnly__
  @property isPartOfKey {Boolean}
  **/

  /**
  Whether this property is an 'unmapped' property.

  __readOnly__
  @property isUnmapped {Boolean}
  **/

  /**
  __Describe this__

  __readOnly__
  @property concurrencyMode {String}
  **/

  /**
  The maximum length for the value of this property.

  __readOnly__
  @property maxLength {Number}
  **/

  /**
  The {{#crossLink "Validator"}}{{/crossLink}}s that are associated with this property. Validators can be added and
  removed from this collection.

  __readOnly__
  @property validators {Array of Validator}
  **/

  /**
  The default value for this property.

  __readOnly__
  @property defaultValue {any}
  **/

  /**
  The navigation property related to this property.  Will only be set if this is a foreign key property.

  __readOnly__
  @property relatedNavigationProperty {NavigationProperty}
  **/

  /**
  A free form object that can be used to define any custom metadata for this DataProperty.

  __readOnly__
  @property custom {Object}
  **/

  /**
  Is this a DataProperty? - always true here
  Allows polymorphic treatment of DataProperties and NavigationProperties.

  __readOnly__
  @property isDataProperty {Boolean}
  **/

  /**
  Is this a NavigationProperty? - always false here
  Allows polymorphic treatment of DataProperties and NavigationProperties.

  __readOnly__
  @property isNavigationProperty {Boolean}
  **/

  proto.isDataProperty = true;
  proto.isNavigationProperty = false;

  proto.resolveProperty = function (propName) {
    var result = this[propName];
    var baseProp = this.baseProperty;
    while (result == undefined && baseProp != null) {
      result = baseProp[propName];
      baseProp = baseProp.baseProperty;
    }
    return result;
  }

  proto.formatName = function () {
    return this.parentType.name + "--" + this.name;
  }


  /**
  General purpose property set method
  @example
      // assume em1 is an EntityManager
      var prop = myEntityType.getProperty("myProperty");
      prop.setProperties( {
          custom: { foo: 7, bar: "test" }
      });
  @method setProperties
  @param config [object]
  @param [config.custom] {Object}
  **/
  proto.setProperties = function (config) {
    assertConfig(config)
        .whereParam("displayName").isOptional()
        .whereParam("custom").isOptional()
        .applyAll(this);
  };

  proto.getAllValidators = function () {
    var validators = this.validators.slice(0);
    var baseProp = this.baseProperty;
    while (baseProp) {
      validators.push.apply(validators, baseProp.validators);
      baseProp = baseProp.baseProperty;
    }
    return validators;
  }

  proto.toJSON = function () {
    // do not serialize dataTypes that are complexTypes
    return __toJson(this, {
      name: null,
      dataType: function (v) {
        return (v && v.parentEnum) ? v.name : undefined;
      }, // do not serialize dataTypes that are complexTypes
      complexTypeName: null,
      isNullable: true,
      defaultValue: null,
      isPartOfKey: false,
      isUnmapped: false,
      isSettable: true,
      concurrencyMode: null,
      maxLength: null,
      validators: null,
      displayName: null,
      enumType: null,
      rawTypeName: null,
      isScalar: true,
      custom: null
    });
  };

  ctor.fromJSON = function (json) {
    json.dataType = DataType.fromName(json.dataType);
    // Parse default value into correct data type. (dateTime instances require extra work to deserialize properly.)
    if (json.defaultValue && json.dataType && json.dataType.parse) {
      json.defaultValue = json.dataType.parse(json.defaultValue, typeof json.defaultValue);
    }

    if (json.validators) {
      json.validators = json.validators.map(Validator.fromJSON);
    }

    return new DataProperty(json);
  };

  return ctor;
})();

var NavigationProperty = (function () {

  /**
  A NavigationProperty describes the metadata for a single property of an  {{#crossLink "EntityType"}}{{/crossLink}} that return instances of other EntityTypes.

  Instances of the NavigationProperty class are constructed automatically during Metadata retrieval.   However it is also possible to construct them
  directly via the constructor.
  @class NavigationProperty
  **/

  /**
  @example
      var homeAddressProp = new NavigationProperty( {
          name: "homeAddress",
          entityTypeName: "Address:#myNamespace",
          isScalar: true,
          associationName: "address_person",
          foreignKeyNames: ["homeAddressId"]
      });
      var homeAddressIdProp = new DataProperty( {
          name: "homeAddressId"
          dataType: DataType.Integer
      });
      // assuming personEntityType is a newly constructed EntityType
      personEntityType.addProperty(homeAddressProp);
      personEntityType.addProperty(homeAddressIdProp);
  @method <ctor> NavigationProperty
  @param config {configuration Object}
  @param [config.name] {String}  The name of this property.
  @param [config.nameOnServer] {String} Same as above but the name is that defined on the server.
  Either this or the 'name' above must be specified. Whichever one is specified the other will be computed using
  the NamingConvention on the MetadataStore associated with the EntityType to which this will be added.
  @param config.entityTypeName {String} The fully qualified name of the type of entity that this property will return.  This type
  need not yet have been created, but it will need to get added to the relevant MetadataStore before this EntityType will be 'complete'.
  The entityType name is constructed as: {shortName} + ":#" + {namespace}
  @param [config.isScalar=true] {Boolean}
  @param [config.associationName] {String} A name that will be used to connect the two sides of a navigation. May be omitted for unidirectional navigations.
  @param [config.foreignKeyNames] {Array of String} An array of foreign key names. The array is needed to support the possibility of multipart foreign keys.
  Most of the time this will be a single foreignKeyName in an array.
  @param [config.foreignKeyNamesOnServer] {Array of String} Same as above but the names are those defined on the server. Either this or 'foreignKeyNames' must
  be specified, if there are foreignKeys. Whichever one is specified the other will be computed using
  the NamingConvention on the MetadataStore associated with the EntityType to which this will be added.
  @param [config.validators] {Array of Validator}
  **/
  var ctor = function NavigationProperty(config) {
    assertConfig(config)
        .whereParam("name").isString().isOptional()
        .whereParam("nameOnServer").isString().isOptional()
        .whereParam("entityTypeName").isString()
        .whereParam("isScalar").isBoolean().isOptional().withDefault(true)
        .whereParam("associationName").isString().isOptional()
        .whereParam("foreignKeyNames").isArray().isString().isOptional().withDefault([])
        .whereParam("foreignKeyNamesOnServer").isArray().isString().isOptional().withDefault([])
        .whereParam("invForeignKeyNames").isArray().isString().isOptional().withDefault([])
        .whereParam("invForeignKeyNamesOnServer").isArray().isString().isOptional().withDefault([])
        .whereParam("validators").isInstanceOf(Validator).isArray().isOptional().withDefault([])
        .whereParam("displayName").isOptional()
        .whereParam("custom").isOptional()
        .applyAll(this);
    var hasName = !!(this.name || this.nameOnServer);

    if (!hasName) {
      throw new Error("A Navigation property must be instantiated with either a 'name' or a 'nameOnServer' property");
    }
  };
  var proto = ctor.prototype;
  proto._$typeName = "NavigationProperty";

  /**
  The {{#crossLink "EntityType"}}{{/crossLink}} that this property belongs to. ( same as parentEntityType).
  __readOnly__
  @property parentType {EntityType}
  **/

  /**
  The {{#crossLink "EntityType"}}{{/crossLink}} that this property belongs to.
  __readOnly__
  @property parentEntityType {EntityType}
  **/

  /**
  The name of this property

  __readOnly__
  @property name {String}
  **/

  /**
  The display name of this property

  __readOnly__
  @property displayName {String} 
  **/
  
  /**
  The name of this property on the server

  __readOnly__
  @property nameOnServer {String} 
  **/
  
  /**
  The {{#crossLink "EntityType"}}{{/crossLink}} returned by this property.

  __readOnly__
  @property entityType {EntityType}
  **/

  /**
  Whether this property returns a single entity or an array of entities.

  __readOnly__
  @property isScalar {Boolean}
  **/

  /**
  Property on the base type that this property is inherited from. Will be null if the property is not on the base type.

  __readOnly__
  @property baseProperty {NavigationProperty}
  **/

  /**
  The name of the association to which that this property belongs.  This associationName will be shared with this
  properties 'inverse'.

  __readOnly__
  @property associationName {String}
  **/

  /**
  The names of the foreign key DataProperties associated with this NavigationProperty. There will usually only be a single DataProperty associated
  with a Navigation property except in the case of entities with multipart keys.

  __readOnly__
  @property foreignKeyNames {Array of String}
  **/

  /**
  The 'foreign key' DataProperties associated with this NavigationProperty. There will usually only be a single DataProperty associated
  with a Navigation property except in the case of entities with multipart keys.

  __readOnly__
  @property relatedDataProperties {Array of DataProperty}
  **/

  /**
  The inverse of this NavigationProperty.  The NavigationProperty that represents a navigation in the opposite direction
  to this NavigationProperty.

  __readOnly__
  @property inverse {NavigationProperty}
  **/

  /**
  The {{#crossLink "Validator"}}{{/crossLink}}s that are associated with this property. Validators can be added and
  removed from this collection.

  __readOnly__
  @property validators {Array of Validator}
  **/

  /**
  A free form object that can be used to define any custom metadata for this NavigationProperty.

  __readOnly__
  @property custom {Object}
  **/

  /**
  Is this a DataProperty? - always false here
  Allows polymorphic treatment of DataProperties and NavigationProperties.

  __readOnly__
  @property isDataProperty {Boolean}
  **/

  /**
  Is this a NavigationProperty? - always true here
  Allows polymorphic treatment of DataProperties and NavigationProperties.

  __readOnly__
  @property isNavigationProperty {Boolean}
  **/

  proto.isDataProperty = false;
  proto.isNavigationProperty = true;

  __extend(proto, DataProperty.prototype, [
    "formatName", "getAllValidators", "resolveProperty"
  ]);

  /**
  General purpose property set method
  @example
      // assume myEntityType is an EntityType
      var prop = myEntityType.getProperty("myProperty");
      prop.setProperties( {
          custom: { foo: 7, bar: "test" }
      });
  @method setProperties
  @param config [object]
  @param [config.inverse] {String}
  @param [config.custom] {Object}
  **/
  proto.setProperties = function (config) {
    if (!this.parentType) {
      throw new Error("Cannot call NavigationProperty.setProperties until the parent EntityType of the NavigationProperty has been set.");
    }
    var inverse = config.inverse;
    if (inverse) delete config.inverse;
    assertConfig(config)
        .whereParam("displayName").isOptional()
        .whereParam("foreignKeyNames").isArray().isString().isOptional().withDefault([])
        .whereParam("invForeignKeyNames").isArray().isString().isOptional().withDefault([])
        .whereParam("custom").isOptional()
        .applyAll(this);
    this.parentType._updateNames(this);

    this._resolveNp();
    if (inverse) {
      this.setInverse(inverse);
    }

  };

  proto.getInverse = function() {
    var np = this;
    while(!np.inverse && np.baseProperty) {
      np = np.baseProperty;
    }
    return np.inverse;
  }

  proto.setInverse = function (inverseNp) {
    var invNp;
    if (typeof (inverseNp) === "string") {
      invNp = this.entityType.getNavigationProperty(inverseNp);
    } else {
      invNp = inverseNp;
    }

    if (!invNp) {
      throw throwSetInverseError(this, "Unable to find inverse property: " + invNpName);
    }
    if (this.inverse || invNp.inverse) {
      throwSetInverseError(this, "It has already been set on one side or the other.");
    }
    if (invNp.entityType != this.parentType) {
      throwSetInverseError(this, invNp.formatName + " is not a valid inverse property for this.");
    }
    if (this.associationName) {
      invNp.associationName = this.associationName;
    } else {
      if (!invNp.associationName) {
        invNp.associationName = this.formatName() + "_" + invNp.formatName();
      }
      this.associationName = invNp.associationName;
    }
    this._resolveNp();
    invNp._resolveNp();
  };

  // In progress - will be used for manual metadata config
  proto.createInverse = function (config) {

    if (!this.entityType) {
      throwCreateInverseError(this, "has not yet been defined.");
    }
    if (this.entityType.isFrozen) {
      throwCreateInverseError(this, "is frozen.");
    }
    var metadataStore = this.entityType.metadataStore;
    if (metadataStore == null) {
      throwCreateInverseError(this, "has not yet been added to the metadataStore.");
    }

    config.entityTypeName = this.parentEntityType.name;
    config.associationName = this.associationName;
    var invNp = new NavigationProperty(config);
    this.parentEntityType.addNavigationProperty(invNp);
    return invNp;
  };

  function throwSetInverseError(np, message) {
    throw new Error("Cannot set the inverse property for: " + np.formatName() + ". " + message);
  }

  function throwCreateInverseError(np, message) {
    throw new Error("Cannot create inverse for: " + np.formatName() + ". The entityType for this navigation property " + message);
  }

  proto.toJSON = function () {
    return __toJson(this, {
      name: null,
      entityTypeName: null,
      isScalar: null,
      associationName: null,
      validators: null,
      displayName: null,
      foreignKeyNames: null,
      invForeignKeyNames: null,
      custom: null
    });
  };

  ctor.fromJSON = function (json) {
    if (json.validators) {
      json.validators = json.validators.map(Validator.fromJSON);
    }
    return new NavigationProperty(json);
  };

  proto._resolveNp = function () {
    var np = this;
    var entityType = np.entityType;
    var invNp = __arrayFirst(entityType.navigationProperties, function (altNp) {
      // Can't do this because of possibility of comparing a base class np with a subclass altNp.
      // return altNp.associationName === np.associationName
      //    && altNp !== np;
      // So use this instead.
      return altNp.associationName === np.associationName &&
          (altNp.name !== np.name || altNp.entityTypeName !== np.entityTypeName);
    });
    np.inverse = invNp;
    //if (invNp && invNp.inverse == null) {
    //    invNp._resolveNp();
    //}
    if (!invNp) {
      // unidirectional 1-n relationship
      np.invForeignKeyNames.forEach(function (invFkName) {
        var fkProp = entityType.getDataProperty(invFkName);
        if (!fkProp) {
          throw new Error("EntityType '" + np.entityTypeName + "' has no foreign key matching '" + invFkName + "'");
        }
        var invEntityType = np.parentType;
        fkProp.inverseNavigationProperty = __arrayFirst(invEntityType.navigationProperties, function (np2) {
          return np2.invForeignKeyNames && np2.invForeignKeyNames.indexOf(fkProp.name) >= 0 && np2.entityType === fkProp.parentType;
        });
        __arrayAddItemUnique(entityType.foreignKeyProperties, fkProp);
      });
    }

    resolveRelated(np);
  }

  // sets navigation property: relatedDataProperties and dataProperty: relatedNavigationProperty
  function resolveRelated(np) {

    var fkNames = np.foreignKeyNames;
    if (fkNames.length === 0) return;

    var parentEntityType = np.parentType;
    var fkProps = fkNames.map(function (fkName) {
      return parentEntityType.getDataProperty(fkName);
    });
    var fkPropCollection = parentEntityType.foreignKeyProperties;

    fkProps.forEach(function (dp) {
      __arrayAddItemUnique(fkPropCollection, dp);
      dp.relatedNavigationProperty = np;
      // now update the inverse
      __arrayAddItemUnique(np.entityType.inverseForeignKeyProperties, dp);
      if (np.relatedDataProperties) {
        __arrayAddItemUnique(np.relatedDataProperties, dp);
      } else {
        np.relatedDataProperties = [dp];
      }
    });
  }

  return ctor;
})();

var AutoGeneratedKeyType = (function () {
  /**
  AutoGeneratedKeyType is an 'Enum' containing all of the valid states for an automatically generated key.
  @class AutoGeneratedKeyType
  @static
  @final
  **/
  var ctor = new Enum("AutoGeneratedKeyType");
  /**
  This entity does not have an autogenerated key.
  The client must set the key before adding the entity to the EntityManager
  @property None {AutoGeneratedKeyType}
  @final
  @static
  **/
  ctor.None = ctor.addSymbol();
  /**
  This entity's key is an Identity column and is set by the backend database.
  Keys for new entities will be temporary until the entities are saved at which point the keys will
  be converted to their 'real' versions.
  @property Identity {AutoGeneratedKeyType}
  @final
  @static
  **/
  ctor.Identity = ctor.addSymbol();
  /**
  This entity's key is generated by a KeyGenerator and is set by the backend database.
  Keys for new entities will be temporary until the entities are saved at which point the keys will
  be converted to their 'real' versions.
  @property KeyGenerator {AutoGeneratedKeyType}
  @final
  @static
  **/
  ctor.KeyGenerator = ctor.addSymbol();
  ctor.resolveSymbols();

  return ctor;
})();

// mixin methods
(function () {

  var proto = Param.prototype;

  proto.isEntity = function () {
    return this._addContext({
      fn: isEntity,
      msg: " must be an entity"
    });
  };

  function isEntity(context, v) {
    if (v == null) return false;
    return (v.entityType !== undefined);
  }

  proto.isEntityProperty = function () {
    return this._addContext({
      fn: isEntityProperty,
      msg: " must be either a DataProperty or a NavigationProperty"
    });
  };

  function isEntityProperty(context, v) {
    if (v == null) return false;
    return (v.isDataProperty || v.isNavigationProperty);
  }
})();

// functions shared between classes related to Metadata

function parseTypeName(entityTypeName) {
  if (!entityTypeName) {
    return null;
  }

  var typeParts = entityTypeName.split(":#");
  if (typeParts.length > 1) {
    return makeTypeHash(typeParts[0], typeParts[1]);
  }

  if (__stringStartsWith(entityTypeName, MetadataStore.ANONTYPE_PREFIX)) {
    var typeHash = makeTypeHash(entityTypeName);
    typeHash.isAnonymous = true
    return typeHash;
  }
  var entityTypeNameNoAssembly = entityTypeName.split(",")[0];
  var typeParts = entityTypeNameNoAssembly.split(".");
  if (typeParts.length > 1) {
    var shortName = typeParts[typeParts.length - 1];
    var namespaceParts = typeParts.slice(0, typeParts.length - 1);
    var ns = namespaceParts.join(".");
    return makeTypeHash(shortName, ns);
  } else {
    return makeTypeHash(entityTypeName);
  }
}

function makeTypeHash(shortName, namespace) {
  return {
    shortTypeName: shortName,
    namespace: namespace,
    typeName: qualifyTypeName(shortName, namespace)
  };
}

function isQualifiedTypeName(entityTypeName) {
  return entityTypeName.indexOf(":#") >= 0;
}

function qualifyTypeName(shortName, namespace) {
  if (namespace && namespace.length > 0) {
    return shortName + ":#" + namespace;
  } else {
    return shortName;
  }
}

// Used by both ComplexType and EntityType
function addProperties(entityType, propObj, ctor) {

  if (!propObj) return;
  if (Array.isArray(propObj)) {
    propObj.forEach(entityType._addPropertyCore.bind(entityType));
  } else if (typeof (propObj) === 'object') {
    for (var key in propObj) {
      if (__hasOwnProperty(propObj, key)) {
        var value = propObj[key];
        value.name = key;
        var prop = new ctor(value);
        entityType._addPropertyCore(prop);
      }
    }
  } else {
    throw new Error("The 'dataProperties' or 'navigationProperties' values must be either an array of data/nav properties or an object where each property defines a data/nav property");
  }
}

breeze.MetadataStore = MetadataStore;
breeze.EntityType = EntityType;
breeze.ComplexType = ComplexType;
breeze.DataProperty = DataProperty;
breeze.NavigationProperty = NavigationProperty;
breeze.AutoGeneratedKeyType = AutoGeneratedKeyType;



;/**
@module breeze
**/

var KeyGenerator = (function () {

  /*
  @class KeyGenerator
  */
  var ctor = function KeyGenerator() {
    // key is dataProperty.name + || + entityType.name, value is propEntry
    // propEntry = { entityType, propertyName, keyMap }
    // keyMap has key of the actual value ( as a string) and a value of null or the real id.
    this._tempIdMap = {};
  };
  var proto = ctor.prototype;

  /*
  Returns a unique 'temporary' id for the specified {{#crossLink "EntityType"}}{{/crossLink}}.
  Uniqueness is defined for this purpose as being unique within each instance of a KeyGenerator. This is sufficient
  because each EntityManager will have its own instance of a KeyGenerator and any entities imported into
  the EntityManager with temporary keys will have them regenerated and remapped on import.

  The return value of this method must be of the correct type as determined by the keyProperties of the
  specified EntityType
  @example
      // Assume em1 is a preexisting EntityManager
      var custType = em1.metadataStore.getEntityType("Customer");
      var cust1 = custType.createEntity();
      // next line both sets cust1's 'CustomerId' property but also returns the value
      var cid1 = em1.generateTempKeyValue(cust1);
      em1.saveChanges().then( function( data) {
        var sameCust1 = data.results[0];
        // cust1 === sameCust1;
        // but cust1.getProperty("CustomerId") != cid1
        // because the server will have generated a new id
        // and the client will have been updated with this
        // new id.
      });
  @method generateTempKeyValue
  @param entityType {EntityType}
  */
  proto.generateTempKeyValue = function (entityType, valueIfAvail) {
    var keyProps = entityType.keyProperties;
    if (keyProps.length > 1) {
      throw new Error("Ids can not be autogenerated for entities with multipart keys");
    }
    var keyProp = keyProps[0];
    var propEntry = getPropEntry(this, keyProp, true);
    var nextId;
    if (valueIfAvail != null) {
      if (!propEntry.keyMap[valueIfAvail.toString()]) {
        nextId = valueIfAvail;
      }
    }

    if (nextId === undefined) {
      var dataType = keyProp.dataType;
      if (dataType.getNext) {
        nextId = dataType.getNext(this);
        // need to watch out for collision with previously imported ids that might also get generated.
        while (propEntry.keyMap[nextId.toString()] != null) {
          nextId = dataType.getNext(this);
        }
      } else {
        throw new Error("Cannot use a property with a dataType of: " + dataType.toString() + " for id generation");
      }
    }

    propEntry.keyMap[nextId.toString()] = true;
    return nextId;
  };

  proto.getTempKeys = function () {
    var results = [];
    //noinspection JSHint
    for (var key in this._tempIdMap) {
      var propEntry = this._tempIdMap[key];
      var entityType = propEntry.entityType;
      // var propName = propEntry.propertyName;
      //noinspection JSHint
      for (var keyValue in propEntry.keyMap) {
        results.push(new EntityKey(entityType, [keyValue]));
      }
    }
    return results;
  };


  // proto methods below are not part of the KeyGenerator interface.

  proto.isTempKey = function (entityKey) {
    var keyProps = entityKey.entityType.keyProperties;
    if (keyProps.length > 1) return false;
    var keyProp = keyProps[0];
    var propEntry = getPropEntry(this, keyProp);
    if (!propEntry) {
      return false;
    }
    return (propEntry.keyMap[entityKey.values[0].toString()] !== undefined);
  };

  function getPropEntry(that, keyProp, createIfMissing) {
    var key = keyProp.name + ".." + keyProp.parentType.name;
    var propEntry = that._tempIdMap[key];
    if (!propEntry) {
      if (createIfMissing) {
        propEntry = { entityType: keyProp.parentType, propertyName: keyProp.name, keyMap: {} };
        that._tempIdMap[key] = propEntry;
      }
    }
    return propEntry;
  }

  __config.registerType(ctor, "KeyGenerator");

  return ctor;
})();

breeze.KeyGenerator = KeyGenerator;;/**
@module breeze
**/

var LocalQueryComparisonOptions = (function () {

  /**
  A LocalQueryComparisonOptions instance is used to specify the "comparison rules" used when performing "local queries" in order
  to match the semantics of these same queries when executed against a remote service.  These options should be set based on the
  manner in which your remote service interprets certain comparison operations.

  The default LocalQueryComparisonOptions stipulates 'caseInsensitive" queries with ANSI SQL rules regarding comparisons of unequal
  length strings.

  @class LocalQueryComparisonOptions
  **/

  /**
  LocalQueryComparisonOptions constructor
  @example
      // create a 'caseSensitive - non SQL' instance.
      var lqco = new LocalQueryComparisonOptions({
              name: "caseSensitive-nonSQL"
              isCaseSensitive: true;
              usesSql92CompliantStringComparison: false;
          });
      // either apply it globally
      lqco.setAsDefault();
      // or to a specific MetadataStore
      var ms = new MetadataStore({ localQueryComparisonOptions: lqco });
      var em = new EntityManager( { metadataStore: ms });

  @method <ctor> LocalQueryComparisonOptions
  @param config {Object}
  @param [config.name] {String}
  @param [config.isCaseSensitive] {Boolean} Whether predicates that involve strings will be interpreted in a "caseSensitive" manner. Default is 'false'
  @param [config.usesSql92CompliantStringComparison] {Boolean} Whether of not to enforce the ANSI SQL standard
  of padding strings of unequal lengths before comparison with spaces. Note that per the standard, padding only occurs with equality and
  inequality predicates, and not with operations like 'startsWith', 'endsWith' or 'contains'.  Default is true.
  **/

  var ctor = function LocalQueryComparisonOptions(config) {
    assertConfig(config || {})
        .whereParam("name").isOptional().isString()
        .whereParam("isCaseSensitive").isOptional().isBoolean()
        .whereParam("usesSql92CompliantStringComparison").isBoolean()
        .applyAll(this);
    if (!this.name) {
      this.name = __getUuid();
    }
    __config._storeObject(this, proto._$typeName, this.name);
  };
  var proto = ctor.prototype;
  proto._$typeName = "LocalQueryComparisonOptions";

  
  /**
  Case insensitive SQL compliant options - this is also the default unless otherwise changed.
  @property caseInsensitiveSQL {LocalQueryComparisonOptions}
  @static
  **/
  ctor.caseInsensitiveSQL = new ctor({
  name: "caseInsensitiveSQL",
  isCaseSensitive: false,
  usesSql92CompliantStringComparison: true
  });

  /**
  The default value whenever LocalQueryComparisonOptions are not specified. By default this is 'caseInsensitiveSQL'.
  @property defaultInstance {LocalQueryComparisonOptions}
  @static
  **/
  ctor.defaultInstance = new ctor(ctor.caseInsensitiveSQL);

  /**
  Sets the 'defaultInstance' by creating a copy of the current 'defaultInstance' and then applying all of the properties of the current instance.
  The current instance is returned unchanged.
  @method setAsDefault
  @example
      var lqco = new LocalQueryComparisonOptions({
          isCaseSensitive: false;
          usesSql92CompliantStringComparison: true;
      });
  lqco.setAsDefault();
  @chainable
  **/
  proto.setAsDefault = function () {
  return __setAsDefault(this, ctor);
  };


  return ctor;
})();

breeze.LocalQueryComparisonOptions = LocalQueryComparisonOptions;


;/**
@module breeze
**/

var NamingConvention = (function () {
  /**
  A NamingConvention instance is used to specify the naming conventions under which a MetadataStore
  will translate property names between the server and the javascript client.

  The default NamingConvention does not perform any translation, it simply passes property names thru unchanged.

  @class NamingConvention
  **/

  /**
  NamingConvention constructor

  @example
      // A naming convention that converts the first character of every property name to uppercase on the server
      // and lowercase on the client.
      var namingConv = new NamingConvention({
          serverPropertyNameToClient: function(serverPropertyName) {
              return serverPropertyName.substr(0, 1).toLowerCase() + serverPropertyName.substr(1);
          },
          clientPropertyNameToServer: function(clientPropertyName) {
              return clientPropertyName.substr(0, 1).toUpperCase() + clientPropertyName.substr(1);
          }            
      });
      var ms = new MetadataStore({ namingConvention: namingConv });
      var em = new EntityManager( { metadataStore: ms });
  @method <ctor> NamingConvention
  @param config {Object}
  @param config.serverPropertyNameToClient {Function} Function that takes a server property name add converts it into a client side property name.
  @param config.clientPropertyNameToServer {Function} Function that takes a client property name add converts it into a server side property name.
  **/
  var ctor = function NamingConvention(config) {
    assertConfig(config || {})
        .whereParam("name").isOptional().isString()
        .whereParam("serverPropertyNameToClient").isFunction()
        .whereParam("clientPropertyNameToServer").isFunction()
        .applyAll(this);
    if (!this.name) {
      this.name = __getUuid();
    }
    __config._storeObject(this, proto._$typeName, this.name);
  };
  var proto = ctor.prototype;
  proto._$typeName = "NamingConvention";

  /**
  The function used to convert server side property names to client side property names.

  @method serverPropertyNameToClient
  @param serverPropertyName {String}
  @param [property] {DataProperty|NavigationProperty} The actual DataProperty or NavigationProperty corresponding to the property name.
  @return {String} The client side property name.
  **/

  /**
  The function used to convert client side property names to server side property names.

  @method clientPropertyNameToServer
  @param clientPropertyName {String}
  @param [property] {DataProperty|NavigationProperty} The actual DataProperty or NavigationProperty corresponding to the property name.
  @return {String} The server side property name.
  **/

  /**
  A noop naming convention - This is the default unless another is specified.
  @property none {NamingConvention}
  @static
  **/
  ctor.none = new ctor({
    name: "noChange",
    serverPropertyNameToClient: function (serverPropertyName) {
      return serverPropertyName;
    },
    clientPropertyNameToServer: function (clientPropertyName) {
      return clientPropertyName;
    }
  });

  /**
  The "camelCase" naming convention - This implementation only lowercases the first character of the server property name
  but leaves the rest of the property name intact.  If a more complicated version is needed then one should be created via the ctor.
  @property camelCase {NamingConvention}
  @static
  **/
  ctor.camelCase = new ctor({
    name: "camelCase",
    serverPropertyNameToClient: function (serverPropertyName) {
      return serverPropertyName.substr(0, 1).toLowerCase() + serverPropertyName.substr(1);
    },
    clientPropertyNameToServer: function (clientPropertyName) {
      return clientPropertyName.substr(0, 1).toUpperCase() + clientPropertyName.substr(1);
    }
  });

  /**
  The default value whenever NamingConventions are not specified.
  @property defaultInstance {NamingConvention}
  @static
  **/
  ctor.defaultInstance = new ctor(ctor.none);

  /**
  Sets the 'defaultInstance' by creating a copy of the current 'defaultInstance' and then applying all of the properties of the current instance.
  The current instance is returned unchanged.
  @method setAsDefault
  @example
      var namingConv = new NamingConvention({
          serverPropertyNameToClient: function(serverPropertyName) {
              return serverPropertyName.substr(0, 1).toLowerCase() + serverPropertyName.substr(1);
          },
          clientPropertyNameToServer: function(clientPropertyName) {
              return clientPropertyName.substr(0, 1).toUpperCase() + clientPropertyName.substr(1);
          }            
      });
      namingConv.setAsDefault();
  @chainable
  **/
  proto.setAsDefault = function () {
    return __setAsDefault(this, ctor);
  };

  return ctor;
})();

breeze.NamingConvention = NamingConvention;


;var Predicate = (function () {
  
  var Predicate = (function () {
    /**
    Used to define a 'where' predicate for an EntityQuery.  Predicates are immutable, which means that any
    method that would modify a Predicate actually returns a new Predicate.
    @class Predicate
    **/

    /**
    Predicate constructor
    @example
        var p1 = new Predicate("CompanyName", "StartsWith", "B");
        var query = new EntityQuery("Customers").where(p1); 
    or
    @example
        var p2 = new Predicate("Region", FilterQueryOp.Equals, null);
        var query = new EntityQuery("Customers").where(p2);
    @method <ctor> Predicate
    @param property {String} A property name, a nested property name or an expression involving a property name.
    @param operator {FilterQueryOp|String}
    @param value {Object} - This will be treated as either a property expression or a literal depending on context.  In general,
    if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
    In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value'
    property and an 'isLiteral' property set to either true or false.  Breeze also tries to infer the dataType of any
    literal based on context, if this fails you can force this inference by making the value argument an object with a
    'value' property and a 'dataType' property set to one of the breeze.DataType enumeration instances.
    **/

    var ctor = function () {
      // empty ctor is used by all subclasses.
      if (arguments.length === 0) return;
      if (arguments.length === 1) {
        // 3 possibilities:
        //      Predicate(aPredicate)
        //      Predicate([ aPredicate ])
        //      Predicate(["freight", ">", 100"])
        //      Predicate( "freight gt 100" }  // passthru ( i.e. maybe an odata string)
        //      Predicate( { freight: { ">": 100 } })
        var arg = arguments[0];
        if (Array.isArray(arg)) {
          if (arg.length === 1) {
            // recurse
            return Predicate(arg[0]);
          } else {
            return createPredicateFromArray(arg);
          }
        } else if (arg instanceof Predicate) {
          return arg;
        } else if (typeof arg == 'string') {
          return new PassthruPredicate(arg);
        } else {
          return createPredicateFromObject(arg);
        }
      } else {
        // 2 possibilities
        //      Predicate("freight", ">", 100");
        //      Predicate("orders", "any", "freight",  ">", 950);
        return createPredicateFromArray(Array.prototype.slice.call(arguments, 0));
      }
    };
    var proto = ctor.prototype;
    
    /**
    Same as using the ctor.
    @example
        // so 
        var p = Predicate.create(a, b, c);
        // is the same as 
        var p = new Predicate(a, b, c); 
    
    @method create
    @param property {String} A property name, a nested property name or an expression involving a property name.
    @param operator {FilterQueryOp|String}
    @param value {Object} - This will be treated as either a property expression or a literal depending on context.  In general,
    if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
    In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value'
    property and an 'isLiteral' property set to either true or false.  Breeze also tries to infer the dataType of any
    literal based on context, if this fails you can force this inference by making the value argument an object with a
    'value' property and a 'dataType' property set to one of the breeze.DataType enumeration instances.

    @static
    **/
    ctor.create = ctor;
    
    /**
    Creates a 'composite' Predicate by 'and'ing a set of specified Predicates together.
    @example
        var dt = new Date(88, 9, 12);
        var p1 = Predicate.create("OrderDate", "ne", dt);
        var p2 = Predicate.create("ShipCity", "startsWith", "C");
        var p3 = Predicate.create("Freight", ">", 100);
        var newPred = Predicate.and(p1, p2, p3);
    or
    @example
        var preds = [p1, p2, p3];
        var newPred = Predicate.and(preds);
    @method and
    @param predicates* {multiple Predicates|Array of Predicate} Any null or undefined values passed in will be automatically filtered out before constructing the composite predicate.
    @static
    **/
    ctor.and = function () {
      var pred = new AndOrPredicate("and", __arraySlice(arguments));
      // return undefined if empty
      return pred.op && pred;
    };
    
    /**
    Creates a 'composite' Predicate by 'or'ing a set of specified Predicates together.
    @example
        var dt = new Date(88, 9, 12);
        var p1 = Predicate.create("OrderDate", "ne", dt);
        var p2 = Predicate.create("ShipCity", "startsWith", "C");
        var p3 = Predicate.create("Freight", ">", 100);
        var newPred = Predicate.or(p1, p2, p3);
    or
    @example
        var preds = [p1, p2, p3];
        var newPred = Predicate.or(preds);
    @method or
    @param predicates* {multiple Predicates|Array of Predicate} Any null or undefined values passed in will be automatically filtered out before constructing the composite predicate.
    @static
    **/
    ctor.or = function () {
      var pred = new AndOrPredicate("or", __arraySlice(arguments));
      return pred.op && pred;
    };
    
    /**
    Creates a 'composite' Predicate by 'negating' a specified predicate.
    @example
        var p1 = Predicate.create("Freight", "gt", 100);
        var not_p1 = Predicate.not(p1);
    This can also be accomplished using the 'instance' version of the 'not' method
    @example
        var not_p1 = p1.not();
    Both of which would be the same as
    @example
        var not_p1 = Predicate.create("Freight", "le", 100);
    @method not
    @param predicate {Predicate}
    @static
    **/
    ctor.not = function (pred) {
      return pred.not();
    };

    ctor.extendBinaryPredicateFn = function(opMap, visitorFn ) {
      var baseVisitorFn = toFunctionVisitor.binaryPredicate;
      for (var op in (opMap || {})) {
        var config = opMap[op];
        config.visitorFn = visitorFn;
        updateAliasMap(BinaryPredicate.prototype.aliasMap, op, opMap[op])
      }
      if (!toFunctionVisitor.isExtended) {
        toFunctionVisitor.binaryPredicate = function (context, expr1Val, expr2Val) {
          var visitorFn = this.aliasMap[this.op.key].visitorFn;
          if (visitorFn) {
            return visitorFn(context, expr1Val, expr2Val);
          } else {
            return baseVisitorFn(context, expr1Val, expr2Val);
          }
        }
        toFunctionVisitor.isExtended = true;
      }
    };


    /**
    'And's this Predicate with one or more other Predicates and returns a new 'composite' Predicate
    @example
        var dt = new Date(88, 9, 12);
        var p1 = Predicate.create("OrderDate", "ne", dt);
        var p2 = Predicate.create("ShipCity", "startsWith", "C");
        var p3 = Predicate.create("Freight", ">", 100);
        var newPred = p1.and(p2, p3);
    or
    @example
        var preds = [p2, p3];
        var newPred = p1.and(preds);
    The 'and' method is also used to write "fluent" expressions
    @example
        var p4 = Predicate.create("ShipCity", "startswith", "F")
          .and("Size", "gt", 2000);
    @method and
    @param predicates* {multiple Predicates|Array of Predicate} Any null or undefined values passed in will be automatically filtered out before constructing the composite predicate.
    **/
    proto.and = function () {
      return new AndOrPredicate("and", argsForAndOrPredicates(this, arguments));
    };
    
    /**
    'Or's this Predicate with one or more other Predicates and returns a new 'composite' Predicate
    @example
        var dt = new Date(88, 9, 12);
        var p1 = Predicate.create("OrderDate", "ne", dt);
        var p2 = Predicate.create("ShipCity", "startsWith", "C");
        var p3 = Predicate.create("Freight", ">", 100);
        var newPred = p1.or(p2, p3);
    or
    @example
        var preds = [p2, p3];
        var newPred = p1.or(preds);
    The 'or' method is also used to write "fluent" expressions
    @example
        var p4 = Predicate.create("ShipCity", "startswith", "F")
          .or("Size", "gt", 2000);
    @method or
    @param predicates* {multiple Predicates|Array of Predicate} Any null or undefined values passed in will be automatically filtered out before constructing the composite predicate.
    **/
    proto.or = function () {
      return new AndOrPredicate("or", argsForAndOrPredicates(this, arguments));
    };
    
    /**
    Returns the 'negated' version of this Predicate
    @example
        var p1 = Predicate.create("Freight", "gt", 100);
        var not_p1 = p1.not();
    This can also be accomplished using the 'static' version of the 'not' method
    @example
        var p1 = Predicate.create("Freight", "gt", 100);
        var not_p1 = Predicate.not(p1);
    which would be the same as
    @example
        var not_p1 = Predicate.create("Freight", "le", 100);
    @method not
    **/
    proto.not = function () {
      return new UnaryPredicate("not", this);
    };
    
    //
    proto.toJSON = function () {
      // toJSON ( part of js standard - takes a single parameter
      // that is either "" or the name of the property being serialized.
      return this.toJSONExt({ entityType: this._entityType });
    }

    proto.toJSONExt = function(context) {
      return this.visit(context, toJSONVisitor);
    }

    proto.toFunction = function(context) {
      return this.visit(context, toFunctionVisitor);
    }

    proto.toString = function () {
      return JSON.stringify(this);
    };

    proto.visit = function(context, visitor) {
      if (__isEmpty(context)) {
        context = { entityType: null };
      } else if (context instanceof EntityType) {
        context = { entityType: context };
      } else if (!__hasOwnProperty(context, "entityType")) {
        throw new Error("All visitor methods must be called with a context object containing at least an 'entityType' property");
      }

      if (visitor) {
        context.visitor = visitor;
      } else {
        visitor = context.visitor;
      }
      var fn = visitor[this.visitorMethodName];
      if (fn == null) {
        throw new Error("Unable to locate method: " + this.visitorMethodName + " on visitor");
      }


      var entityType = context.entityType;
      // don't both validating if already done so ( or if no _validate method
      if (this._validate && entityType == null || this._entityType !== entityType) {
        // don't need to capture return value because validation fn doesn't have one.
        this._validate(entityType, context.usesNameOnServer);
        this._entityType = entityType;
      }

      // args = context, arg1, args2, ...
      var args = Array.prototype.slice.call(arguments, 1);
      return fn.call(this, context);
    }

    proto._initialize = function (visitorMethodName,  opMap) {
      this.visitorMethodName = visitorMethodName;
      var aliasMap = this.aliasMap = {};
      for (var op in (opMap || {})) {
        updateAliasMap(aliasMap, op, opMap[op])
      }
    };

    function argsForAndOrPredicates(obj, args) {
      var preds = args[0];
      if (preds instanceof Predicate) {
        preds = __arraySlice(args);
      } else if (!Array.isArray(preds)) {
        preds = [Predicate(__arraySlice(args))];
      }
      return [obj].concat(preds);
    }
    
    function updateAliasMap(aliasMap, op, config) {
      var key = op.toLowerCase();
      config.key = key;
      aliasMap[key] = config;

      config.aliases && config.aliases.forEach(function (alias) {
        aliasMap[alias.toLowerCase()] = config;
      });
    }
    
    proto._resolveOp = function (op, okIfNotFound) {
      op = op.operator || op;
      var result = this.aliasMap[op.toLowerCase()];
      if (!result && !okIfNotFound) {
        throw new Error("Unable to resolve operator: " + op);
      }
      return result;
    };
    
    function createPredicateFromArray(arr) {
      // TODO: assert that length of the array should be > 3
      // Needs to handle:
      //      [ "freight", ">", 100"];
      //      [ "orders", "any", "freight",  ">", 950 ]
      //      [ "orders", "and", anotherPred ]
      //      [ "orders", "and", [ "freight, ">", 950 ]
      var json = {};
      var value = {};
      json[arr[0]] = value;
      var op = arr[1];
      op = op.operator || op;  // incoming op will be either a string or a FilterQueryOp
      if (arr.length == 3) {
        value[op] = arr[2];
      } else {
        value[op] = createPredicateFromArray(arr.splice(2));
      }
      return createPredicateFromObject(json);
    };
    
    function createPredicateFromObject(obj) {
      if (obj instanceof Predicate) return obj;
      
      if (typeof obj != 'object') {
        throw new Error("Unable to convert to a Predicate: " + obj);
      }
      var keys = Object.keys(obj);
      var preds = keys.map(function (key) {
        return createPredicateFromKeyValue(key, obj[key]);
      });
      return (preds.length === 1) ? preds[0] : new AndOrPredicate("and", preds);
    }
    
    function createPredicateFromKeyValue(key, value) {
      // { and: [a,b] } key='and', value = [a,b]
      if (AndOrPredicate.prototype._resolveOp(key, true)) {
        return new AndOrPredicate(key, value);
      }
      
      // { not: a }  key= 'not', value = a
      if (UnaryPredicate.prototype._resolveOp(key, true)) {
        return new UnaryPredicate(key, value);
      }

      if ((typeof value !== 'object') || value == null || __isDate(value)) {
        // { foo: bar } key='foo', value = bar ( where bar is a literal i.e. a string, a number, a boolean or a date.
        return new BinaryPredicate("eq", key, value);
      } else if (__hasOwnProperty(value, 'value')) {
        // { foo: { value: bar, dataType: xxx} } key='foo', value = bar ( where bar is an object representing a literal
        return new BinaryPredicate("eq", key, value);
      }
      
      if (Array.isArray(value)) {
        throw new Error("Unable to resolve predicate after the phrase: " + key);
      }
      
      var expr = key;
      var keys = Object.keys(value);
      var preds = keys.map(function (op) {
        
        // { a: { any: b } op = 'any', expr=a, value[op] = b
        if (AnyAllPredicate.prototype._resolveOp(op, true)) {
          return new AnyAllPredicate(op, expr, value[op]);
        }
        
        if (BinaryPredicate.prototype._resolveOp(op, true)) {
          // { a: { ">": b }} op = ">", expr=a, value[op] = b
          return new BinaryPredicate(op, expr, value[op]);
        } else if (__hasOwnProperty(value[op], 'value')) {
          // { a: { ">": { value: b, dataType: 'Int32' }} expr = a value[op] = { value: b, dataType: 'Int32' }
          return new BinaryPredicate("eq", expr, value[op]);
        }
        
        var msg = __formatString("Unable to resolve predicate after the phrase: '%1' for operator: '%2'  and value: '%3'", expr, op, value[op]);
        throw new Error(msg);

      });
      
      return (preds.length === 1) ? preds[0] : new AndOrPredicate("and", preds);
    }

    return ctor;
  })();
  
  var PassthruPredicate = (function () {
    var ctor = function PassthruPredicate(value) {
      this.value = value;
    };
    var proto = ctor.prototype = new Predicate();
    proto._initialize('passthruPredicate');

    proto._validate = __noop;

    return ctor;
  })();
  
  var UnaryPredicate = (function () {
    var ctor = function UnaryPredicate(op, pred) {
      this.op = this._resolveOp(op);
      this.pred = Predicate(pred);
    };
    
    var proto = ctor.prototype = new Predicate();
    proto._initialize('unaryPredicate', {
      'not': { aliases: [ '!', '~' ] }
    });

    proto._validate = function(entityType, usesNameOnServer) {
      this.pred._validate(entityType, usesNameOnServer);
    };

    return ctor;
  })();
  
  var BinaryPredicate = (function () {
    var ctor = function BinaryPredicate(op, expr1, expr2) {
      // 5 public props op, expr1Source, expr2Source, expr1, expr2
      this.op = this._resolveOp(op);
      this.expr1Source = expr1;
      this.expr2Source = expr2;
      // this.expr1 and this.expr2 won't be
      // determined until validate is run
    };
    
    var proto = ctor.prototype = new Predicate();
    proto._initialize('binaryPredicate', {
      'eq': {
        aliases: ["==", "equals" ]
      },
      'ne': {
        aliases: ["!=", "~=", "notequals" ]
      },
      'lt': {
        aliases: ["<", "lessthan" ]
      },
      'le': {
        aliases: ["<=", "lessthanorequal" ]
      },
      'gt': {
        aliases: [">", "greaterthan"]
      },
      'ge': {
        aliases: [">=", "greaterthanorequal" ]
      },
      'startswith': {
        isFunction: true
      },
      'endswith': {
        isFunction: true
      },
      'contains': {
        aliases: ["substringof"],
        isFunction: true
      },
      'in': {

      }
    });


    proto._validate = function(entityType, usesNameOnServer) {
      var expr1Context = { entityType: entityType, usesNameOnServer: usesNameOnServer };
      this.expr1 = createExpr(this.expr1Source, expr1Context);
      if (this.expr1 == null) {
        throw new Error("Unable to validate 1st expression: " + this.expr1Source);
      }
      if (this.expr1 instanceof LitExpr) {
        // lhs must be either a property or a function.
        throw new Error("The left hand side of a binary predicate cannot be a literal expression, it must be a valid property or functional predicate expression: " + this.expr1Source);
      }

      if (this.op.key == 'in' && !Array.isArray(this.expr2Source)) {
        throw new Error("The 'in' operator requires that its right hand argument be an array");
      }
      var expr2Context = __extend(expr1Context, { isRHS: true, dataType: this.expr1.dataType });
      this.expr2 = createExpr(this.expr2Source, expr2Context );
      if (this.expr2 == null) {
        throw new Error("Unable to validate 2nd expression: " + this.expr2Source);
      }

      if (this.expr1.dataType == null) {
        this.expr1.dataType = this.expr2.dataType;
      }
    }

    return ctor;
  })();
  
  var AndOrPredicate = (function () {
    // two public props: op, preds
    var ctor = function AndOrPredicate(op, preds) {
      this.op = this._resolveOp(op);
      if (preds.length == 1 && Array.isArray(preds[0])) {
        preds = preds[0];
      }
      this.preds = preds.filter(function (pred) {
        return pred != null;
      }).map(function (pred) {
        return Predicate(pred);
      });
      if (this.preds.length == 0) {
        // marker for an empty predicate
        this.op = null;
      }
      if (this.preds.length == 1) {
        return this.preds[0];
      }
    };
    
    var proto = ctor.prototype = new Predicate();
    proto._initialize("andOrPredicate", {
      'and': { aliases: [ '&&' ] },
      'or': { aliases: [ '||' ] }
    });

    proto._validate = function(entityType, usesNameOnServer) {
      this.preds.every(function (pred) {
        pred._validate(entityType, usesNameOnServer);
      });
    }

    return ctor;
  })();
  
  var AnyAllPredicate = (function () {
    // 4 public props: op, exprSource, expr, pred
    var ctor = function AnyAllPredicate(op, expr, pred) {
      this.op = this._resolveOp(op);
      this.exprSource = expr;
      // this.expr will not be resolved until validate is called
      this.pred = Predicate(pred);
    };
    
    var proto = ctor.prototype = new Predicate();
    proto._initialize("anyAllPredicate", {
      'any': { aliases: ['some'] },
      'all': { aliases: ["every"] }
    });

    proto._validate = function(entityType, usesNameOnServer) {
      this.expr = createExpr(this.exprSource, { entityType: entityType, usesNameOnServer: usesNameOnServer });
      // can't really know the predicateEntityType unless the original entity type was known.
      if (entityType == null || entityType.isAnonymous) {
        this.expr.dataType = null;
      }
      this.pred._validate(this.expr.dataType, usesNameOnServer);
    }

    return ctor;
  })();

  var PredicateExpression = function (visitorMethodName) {
    this.visitorMethodName = visitorMethodName;
    // give expressions the Predicate prototype method
    this.visit = Predicate.prototype.visit;
    // default impls - may be overridden
    this._validate = __noop;
  }

  var LitExpr = (function () {
    // 2 public props: value, dataType
    var ctor = function LitExpr(value, dataType, hasExplicitDataType) {
      // dataType may come is an a string
      dataType = resolveDataType(dataType);
      // if the DataType comes in as Undefined this means
      // that we should NOT attempt to parse it but just leave it alone
      // for now - this is usually because it is part of a Func expr.
      dataType = dataType || DataType.fromValue(value);

      if (dataType && dataType.parse) {
        if (Array.isArray(value)) {
          this.value = value.map(function(v) { return dataType.parse(v, typeof v) });
        } else {
          this.value = dataType.parse(value, typeof value);
        }
      } else {
        this.value = value;
      }
      this.dataType = dataType;
      this.hasExplicitDataType = hasExplicitDataType;
    };
    var proto = ctor.prototype = new PredicateExpression('litExpr');
    proto.toString = function() {
      return " LitExpr - value: " + this.value.toString() + " dataType: " + this.dataType.toString();
    };

    function resolveDataType(dataType) {
      if (dataType == null) return dataType;
      if (DataType.contains(dataType)) {
        return dataType;
      }
      if (__isString(dataType)) {
        var dt = DataType.fromName(dataType);
        if (dt) return dt;
        throw new Error("Unable to resolve a dataType named: " + dataType);
      }
      
      throw new Error("The dataType parameter passed into this literal expression is not a 'DataType'" + dataType);
    }
    
    return ctor;
  })();
  
  var PropExpr = (function () {
    // two public props: propertyPath, dateType
    var ctor = function PropExpr(propertyPath) {
      this.propertyPath = propertyPath;
      //this.dataType = DataType.Undefined;
      // this.dataType resolved after validate ( if not on an anon type }
    };
    var proto = ctor.prototype = new PredicateExpression('propExpr');
    proto.toString = function() {
      return " PropExpr - " + this.propertyPath;
    };

    proto._validate = function(entityType, usesNameOnServer) {

      if (entityType == null || entityType.isAnonymous) return;
      var props = entityType.getPropertiesOnPath(this.propertyPath, usesNameOnServer, false);

      if (!props) {
        var msg = __formatString("Unable to resolve propertyPath.  EntityType: '%1'   PropertyPath: '%2'", entityType.name, this.propertyPath);
        throw new Error(msg);
      }
      // get the last property
      var prop = props[props.length - 1];
      if (prop.isDataProperty) {
        this.dataType = prop.dataType;
      } else {
        this.dataType = prop.entityType;
      }
    }

    return ctor;
  })();
  
  var FnExpr = (function () {
    
    var ctor = function FnExpr(fnName, exprs) {
      // 4 public props: fnName, exprs, localFn, dataType
      this.fnName = fnName;
      this.exprs = exprs;
      var qf = _funcMap[fnName];
      if (qf == null) {
        throw new Error("Unknown function: " + fnName);
      }
      this.localFn = qf.fn;
      this.dataType = qf.dataType;
    };
    var proto = ctor.prototype = new PredicateExpression('fnExpr');

    proto.toString = function() {
      var exprStr = this.exprs.map(function(expr) {
        expr.toString();
      }).toString();
      return "FnExpr - " + this.fnName + "(" + exprStr + ")";
    };

    proto._validate = function(entityType, usesNameOnServer) {
      this.exprs.forEach(function (expr) {
        expr._validate(entityType, usesNameOnServer);
      });
    };

    // TODO: add dataTypes for the args next - will help to infer other dataTypes.
    var _funcMap = ctor.funcMap = {
      toupper: {
        fn: function (source) {
          return source.toUpperCase();
        }, dataType: DataType.String
      },
      tolower: {
        fn: function (source) {
          return source.toLowerCase();
        }, dataType: DataType.String
      },
      substring: {
        fn: function (source, pos, length) {
          return source.substring(pos, length);
        }, dataType: DataType.String
      },
      substringof: {
        fn: function (find, source) {
          return source.indexOf(find) >= 0;
        }, dataType: DataType.Boolean
      },
      length: {
        fn: function (source) {
          return source.length;
        }, dataType: DataType.Int32
      },
      trim: {
        fn: function (source) {
          return source.trim();
        }, dataType: DataType.String
      },
      concat: {
        fn: function (s1, s2) {
          return s1.concat(s2);
        }, dataType: DataType.String
      },
      replace: {
        fn: function (source, find, replace) {
          return source.replace(find, replace);
        }, dataType: DataType.String
      },
      startswith: {
        fn: function (source, find) {
          return __stringStartsWith(source, find);
        }, dataType: DataType.Boolean
      },
      endswith: {
        fn: function (source, find) {
          return __stringEndsWith(source, find);
        }, dataType: DataType.Boolean
      },
      indexof: {
        fn: function (source, find) {
          return source.indexOf(find);
        }, dataType: DataType.Int32
      },
      round: {
        fn: function (source) {
          return Math.round(source);
        }, dataType: DataType.Int32
      },
      ceiling: {
        fn: function (source) {
          return Math.ceil(source);
        }, dataType: DataType.Int32
      },
      floor: {
        fn: function (source) {
          return Math.floor(source);
        }, dataType: DataType.Int32
      },
      second: {
        fn: function (source) {
          return source.getSeconds();
        }, dataType: DataType.Int32
      },
      minute: {
        fn: function (source) {
          return source.getMinutes();
        }, dataType: DataType.Int32
      },
      day: {
        fn: function (source) {
          return source.getDate();
        }, dataType: DataType.Int32
      },
      month: {
        fn: function (source) {
          return source.getMonth() + 1;
        }, dataType: DataType.Int32
      },
      year: {
        fn: function (source) {
          return source.getFullYear();
        }, dataType: DataType.Int32
      }
    };
    
    return ctor;
  })();

  var RX_IDENTIFIER = /^[a-z_][\w.$]*$/i;
  // comma delimited expressions ignoring commas inside of both single and double quotes.
  var RX_COMMA_DELIM1 = /('[^']*'|[^,]+)/g;
  var RX_COMMA_DELIM2 = /("[^"]*"|[^,]+)/g;
  var DELIM = String.fromCharCode(191);

  function createExpr(source, exprContext) {
    var entityType = exprContext.entityType;

    // the right hand side of an 'in' clause
    if (Array.isArray(source)) {
      if (!exprContext.isRHS) {
        throw new Error("Array expressions are only permitted on the right hand side of a BinaryPredicate");
      }
      return new LitExpr(source, exprContext.dataType);
    }

    if (!__isString(source)) {
      if (source != null && __isObject(source) && !source.toISOString) { 
        // source is an object but not a Date-like thing such as a JS or MomentJS Date
        if (source.value === undefined) {
          throw new Error("Unable to resolve an expression for: " + source + " on entityType: " + entityType.name);
        }
        if (source.isProperty) {
          return new PropExpr(source.value);
        } else {
          // we want to insure that any LitExpr created this way is tagged with 'hasExplicitDataType: true'
          // because we want to insure that if we roundtrip thru toJSON that we don't
          // accidentally reinterpret this node as a PropExpr.
          // return new LitExpr(source.value, source.dataType || context.dataType, !!source.dataType);
          return new LitExpr(source.value, source.dataType || exprContext.dataType, true);
        }
      } else {
        return new LitExpr(source, exprContext.dataType);
      }
    }

    if (exprContext.isRHS) {
      if (entityType == null || entityType.isAnonymous) {
        // if entityType is unknown then assume that the rhs is a literal
        return new LitExpr(source, exprContext.dataType);
      } else {
        return parseLitOrPropExpr(source, exprContext);
      }
    } else {
      var regex = /\([^()]*\)/;
      var m;
      var tokens = [];
      var i = 0;
      while (m = regex.exec(source)) {
        var token = m[0];
        tokens.push(token);
        var repl = DELIM + i++;
        source = source.replace(token, repl);
      }

      var expr = parseExpr(source, tokens, exprContext);
      expr._validate(entityType, exprContext.usesNameOnServer);
      return expr;
    }
  }

  function parseExpr(source, tokens, exprContext) {
    var parts = source.split(DELIM);
    if (parts.length === 1) {
      return parseLitOrPropExpr(parts[0], exprContext);
    } else {
      return parseFnExpr(source, parts, tokens, exprContext);
    }
  }

  function parseLitOrPropExpr(value, exprContext) {
    value = value.trim();
    // value is either a string, a quoted string, a number, a bool value, or a date
    // if a string ( not a quoted string) then this represents a property name ( 1st ) or a lit string ( 2nd)
    var firstChar = value.substr(0, 1);
    var isQuoted = (firstChar === "'" || firstChar === '"') && value.length > 1 && value.substr(value.length - 1) === firstChar;
    if (isQuoted) {
      var unquotedValue = value.substr(1, value.length - 2);
      return new LitExpr(unquotedValue, exprContext.dataType || DataType.String);
    } else {
      var entityType = exprContext.entityType;
      // TODO: get rid of isAnonymous below when we get the chance.
      if (entityType == null || entityType.isAnonymous) {
        // this fork will only be reached on the LHS of an BinaryPredicate -
        // a RHS expr cannot get here with an anon type
        return new PropExpr(value);
      } else {
        var mayBeIdentifier = RX_IDENTIFIER.test(value);
        if (mayBeIdentifier) {
          // if (entityType.getProperty(value, false) != null) {
          if (entityType.getPropertiesOnPath(value, exprContext.usesNameOnServer, false) != null) {
            return new PropExpr(value);
          }
        }
      }
      // we don't really know the datatype here because even though it comes in as a string
      // its usually a string BUT it might be a number  i.e. the "1" or the "2" from an expr
      // like "toUpper(substring(companyName, 1, 2))"
      return new LitExpr(value, exprContext.dataType);
    }
  }

  function parseFnExpr(source, parts, tokens, exprContext) {
    try {
      var fnName = parts[0].trim().toLowerCase();

      var argSource = tokens[parts[1]].trim();
      if (argSource.substr(0, 1) === "(") {
        argSource = argSource.substr(1, argSource.length - 2);
      }
      var commaMatchStr = source.indexOf("'") >= 0 ? RX_COMMA_DELIM1 : RX_COMMA_DELIM2;
      var args = argSource.match(commaMatchStr);
      var newContext = __extend({}, exprContext);
      // a dataType of Undefined on a context basically means not to try parsing
      // the value if the expr is a literal
      newContext.dataType = DataType.Undefined;
      newContext.isFnArg = true;
      var exprs = args.map(function (a) {
        return parseExpr(a, tokens, newContext);
      });
      return new FnExpr(fnName, exprs);
    } catch (e) {
      return null;
    }
  }

  var toFunctionVisitor =  (function () {
    var visitor = {

      passthruPredicate: function () {
        throw new Error("Cannot execute an PassthruPredicate expression against the local cache: " + this.value);
      },
      
      unaryPredicate: function (context) {
        var predFn = this.pred.visit(context);
        switch (this.op.key) {
          case "not":
            return function (entity) {
              return !predFn(entity);
            };
          default:
            throw new Error("Invalid unary operator:" + this.op.key);
        }
      },
      
      binaryPredicate: function (context) {
        var expr1Fn = this.expr1.visit(context);
        var expr2Fn = this.expr2.visit(context);
        var dataType = this.expr1.dataType || this.expr2.dataType;
        var lqco = context.entityType.metadataStore.localQueryComparisonOptions;
        var predFn = getBinaryPredicateFn(this, dataType, lqco);
        if (predFn == null) {
          throw new Error("Invalid binaryPredicate operator:" + this.op.key);
        }
        return function (entity) {
          return predFn(expr1Fn(entity), expr2Fn(entity));
        };
      },
      
      andOrPredicate: function (context) {
        var predFns = this.preds.map(function(pred) {
          return pred.visit(context);
        });
        switch (this.op.key) {
          case "and":
            return function (entity) {
              var result = predFns.reduce(function (prev, cur) {
                return prev && cur(entity);
              }, true);
              return result;
            };
          case "or":
            return function (entity) {
              var result = predFns.reduce(function (prev, cur) {
                return prev || cur(entity);
              }, false);
              return result;
            };
          default:
            throw new Error("Invalid boolean operator:" + op.key);
        }
      },
      
      anyAllPredicate: function (context) {
        var exprFn = this.expr.visit(context);
        var newContext = __extend({}, context);
        newContext.entityType = this.expr.dataType;
        var predFn = this.pred.visit(newContext);
        var anyAllPredFn = getAnyAllPredicateFn(this.op);
        return function (entity) {
          return anyAllPredFn(exprFn(entity), predFn);
        };
      },
      
      litExpr: function () {
        var value = this.value;
        return function (entity) {
          return value;
        };
      },
      
      propExpr: function () {
        var propertyPath = this.propertyPath;
        var properties = propertyPath.split('.');
        if (properties.length === 1) {
          return function (entity) {
            return entity.getProperty(propertyPath);
          };
        } else {
          return function (entity) {
            return getPropertyPathValue(entity, properties);
          };
        }
      },
      
      fnExpr: function (context) {
        var exprFns = this.exprs.map(function(expr) {
          return expr.visit(context);
        });
        var that = this;
        return function (entity) {
          var values = exprFns.map(function (exprFn) {
            var value = exprFn(entity);
            return value;
          });
          var result = that.localFn.apply(null, values);
          return result;
        }
      }

    };

    function getAnyAllPredicateFn(op) {
      switch (op.key) {
        case "any":
          return function (v1, v2) {
            return v1.some(function (v) {
              return v2(v);
            });
          };
        case "all":
          return function (v1, v2) {
            return v1.every(function (v) {
              return v2(v);
            });
          };
        default:
          throw new Error("Unknown operator: " + op.key);
      }
    }

    function getBinaryPredicateFn(binaryPredicate, dataType, lqco) {
      var op = binaryPredicate.op;
      var mc = DataType.getComparableFn(dataType);
      var predFn;
      switch (op.key) {
        case 'eq':
          predFn = function (v1, v2) {
            if (v1 && typeof v1 === 'string') {
              return stringEquals(v1, v2, lqco);
            } else {
              return mc(v1) == mc(v2);
            }
          };
          break;
        case 'ne':
          predFn = function (v1, v2) {
            if (v1 && typeof v1 === 'string') {
              return !stringEquals(v1, v2, lqco);
            } else {
              return mc(v1) != mc(v2);
            }
          };
          break;
        case 'gt':
          predFn = function (v1, v2) {
            return mc(v1) > mc(v2);
          };
          break;
        case 'ge':
          predFn = function (v1, v2) {
            return mc(v1) >= mc(v2);
          };
          break;
        case 'lt':
          predFn = function (v1, v2) {
            return mc(v1) < mc(v2);
          };
          break;
        case 'le':
          predFn = function (v1, v2) {
            return mc(v1) <= mc(v2);
          };
          break;
        case 'startswith':
          predFn = function (v1, v2) {
            return stringStartsWith(v1, v2, lqco);
          };
          break;
        case 'endswith':
          predFn = function (v1, v2) {
            return stringEndsWith(v1, v2, lqco);
          };
          break;
        case 'contains':
          predFn = function (v1, v2) {
            return stringContains(v1, v2, lqco);
          };
          break;
        case 'in':
          predFn = function (v1, v2) {
            v1 = mc(v1);
            v2 = v2.map(function(v) { return mc(v) });
            return v2.indexOf(v1) >= 0;
          };
          break;
        default:
          return null;
      }
      return predFn;
    }

    function stringEquals(a, b, lqco) {
      if (b == null) return false;
      if (typeof b !== 'string') {
        b = b.toString();
      }
      if (lqco.usesSql92CompliantStringComparison) {
        a = (a || "").trim();
        b = (b || "").trim();
      }
      if (!lqco.isCaseSensitive) {
        a = (a || "").toLowerCase();
        b = (b || "").toLowerCase();
      }
      return a === b;
    }
    
    function stringStartsWith(a, b, lqco) {
      if (!lqco.isCaseSensitive) {
        a = (a || "").toLowerCase();
        b = (b || "").toLowerCase();
      }
      return __stringStartsWith(a, b);
    }
    
    function stringEndsWith(a, b, lqco) {
      if (!lqco.isCaseSensitive) {
        a = (a || "").toLowerCase();
        b = (b || "").toLowerCase();
      }
      return __stringEndsWith(a, b);
    }
    
    function stringContains(a, b, lqco) {
      if (!lqco.isCaseSensitive) {
        a = (a || "").toLowerCase();
        b = (b || "").toLowerCase();
      }
      return a.indexOf(b) >= 0;
    }
    
    return visitor;
  }());

  var toJSONVisitor = (function () {
    var visitor = {

      passthruPredicate: function () {
        return this.value;
      },
      
      unaryPredicate: function (context) {
        var predVal = this.pred.visit(context);
        var json = {};
        json[this.op.key] = predVal;
        return json;
      },
      
      binaryPredicate: function (context) {
        var expr1Val = this.expr1.visit(context);
        var expr2Val = this.expr2.visit(context);
        var json = {};
        if (this.expr2 instanceof PropExpr) {
          expr2Val = { value: expr2Val, isProperty: true };
        }
        if (this.op.key === "eq") {
          json[expr1Val] = expr2Val;
        } else {
          var value = {};
          json[expr1Val] = value;
          value[this.op.key] = expr2Val;
        }
        return json;
      },
      
      andOrPredicate: function (context) {
        var predVals = this.preds.map(function(pred) {
          return pred.visit(context);
        });
        if (!predVals || !predVals.length) {
          return {};
        }
        var json;
        // normalizeAnd clauses if possible.
        // passthru predicate will appear as string and their 'ands' can't be 'normalized'
        if (this.op.key === 'and' && predVals.length === 2 && !predVals.some(__isString)) {
          // normalize 'and' clauses - will return null if can't be combined.
          json = predVals.reduce(combine);
        }
        if (json == null) {
          json = {};
          json[this.op.key] = predVals;
        }
        return json;
      },
      
      anyAllPredicate: function (context) {
        var exprVal = this.expr.visit(context);
        var newContext = __extend({}, context);
        newContext.entityType = this.expr.dataType;
        var predVal = this.pred.visit(newContext);
        var json = {};
        var value = {};
        value[this.op.key] = predVal;
        json[exprVal] = value;
        return json;
      },
      
      litExpr: function (context) {
        if (this.hasExplicitDataType || context.useExplicitDataType) {
          return { value: this.value, dataType: this.dataType.name }
        } else {
          return this.value;
        }
      },
      
      propExpr: function (context) {
        if (context.toNameOnServer) {
          return context.entityType.clientPropertyPathToServer(this.propertyPath);
        } else {
          return this.propertyPath;
        }
      },
      
      fnExpr: function (context) {
        var exprVals = this.exprs.map(function(expr) {
          return expr.visit(context);
        });
        return this.fnName + "(" + exprVals.join(",") + ")";
      }
    };
    
    function combine(j1, j2) {
      var ok = Object.keys(j2).every(function (key) {
        if (j1.hasOwnProperty(key)) {
          if (typeof (j2[key]) != 'object') {
            // exit and indicate that we can't combine
            return false;
          }
          if (combine(j1[key], j2[key]) == null) {
            return false;
          }
        } else {
          j1[key] = j2[key];
        }
        return true;
      });
      return ok ? j1 : null;
    }
    
    return visitor;
  }());

  
  return Predicate;

})();

breeze.Predicate = Predicate;

;var EntityQuery = (function () {
  /**
  An EntityQuery instance is used to query entities either from a remote datasource or from a local {{#crossLink "EntityManager"}}{{/crossLink}}.

  EntityQueries are immutable - this means that all EntityQuery methods that return an EntityQuery actually create a new EntityQuery.  This means that
  EntityQueries can be 'modified' without affecting any current instances.

  @class EntityQuery
  **/

  /**
  @example
      var query = new EntityQuery("Customers")

  Usually this constructor will be followed by calls to filtering, ordering or selection methods
  @example
      var query = new EntityQuery("Customers")
          .where("CompanyName", "startsWith", "C")
          .orderBy("Region");

  @method <ctor> EntityQuery
  @param [resourceName] {String}
  **/
  var ctor = function EntityQuery(resourceName) {
    if (resourceName != null && !__isString(resourceName)) {
      return fromJSON(this, resourceName);
    }
    
    this.resourceName = resourceName;
    this.fromEntityType = null;
    this.wherePredicate = null;
    this.orderByClause = null;
    this.selectClause = null;
    this.skipCount = null;
    this.takeCount = null;
    this.expandClause = null;
    this.parameters = {};
    this.inlineCountEnabled = false;
    this.noTrackingEnabled = false;
    // default is to get queryOptions and dataService from the entityManager.
    // this.queryOptions = new QueryOptions();
    // this.dataService = new DataService();
    this.entityManager = null;

  };
  var proto = ctor.prototype;
  proto._$typeName = "EntityQuery";
  
  
  /**
  The resource name used by this query.

  __readOnly__
  @property resourceName {String}
  **/

  /**
  The entityType that is associated with the 'from' clause ( resourceName) of the query.  This is only guaranteed to be be set AFTER the query
  has been executed because it depends on the MetadataStore associated with the EntityManager that the query was executed against.
  This value may be null if the entityType cannot be associated with a resourceName.

  __readOnly__
  @property fromEntityType {EntityType}
  **/

  /**
  The entityType that will be returned by this query. This property will only be set if the 'toType' method was called.

  __readOnly__
  @property resultEntityType {EntityType}
  **/

  /**
  The 'where' predicate used by this query.

  __readOnly__
  @property wherePredicate {Predicate}
  **/

  /**
  The {{#crossLink "OrderByClause"}}{{/crossLink}} used by this query.

  __readOnly__
  @property orderByClause {OrderByClause}
  **/

  /**
  The number of entities to 'skip' for this query.

  __readOnly__
  @property skipCount {Integer}
  **/

  /**
  The number of entities to 'take' for this query.

  __readOnly__
  @property takeCount {Integer}
  **/

  /**
  Any additional parameters that were added to the query via the 'withParameters' method.

  __readOnly__
  @property parameters {Object}
  **/

  /**
  The {{#crossLink "QueryOptions"}}{{/crossLink}} for this query.

  __readOnly__
  @property queryOptions {QueryOptions}
  **/

  /**
  The {{#crossLink "EntityManager"}}{{/crossLink}} for this query. This may be null and can be set via the 'using' method.

  __readOnly__
  @property entityManager {EntityManager}
  **/

  /**
  Specifies the resource to query for this EntityQuery.
  @example
      var query = new EntityQuery()
          .from("Customers");
  is the same as
  @example
      var query = new EntityQuery("Customers");
  @method from
  @param resourceName {String} The resource to query.
  @return {EntityQuery}
  @chainable
  **/
  proto.from = function (resourceName) {
    // TODO: think about allowing entityType as well
    assertParam(resourceName, "resourceName").isString().check();
    return clone(this, "resourceName", resourceName);
  };
  
  /**
  This is a static version of the "from" method and it creates a 'base' entityQuery for the specified resource name.
  @example
      var query = EntityQuery.from("Customers");
  is the same as
  @example
      var query = new EntityQuery("Customers");
  @method from
  @static
  @param resourceName {String} The resource to query.
  @return {EntityQuery}
  @chainable
  **/
  ctor.from = function (resourceName) {
    assertParam(resourceName, "resourceName").isString().check();
    return new EntityQuery(resourceName);
  };
  
  /**
  Specifies the top level EntityType that this query will return.  Only needed when a query returns a json result that does not include type information.
  @example
      var query = new EntityQuery()
        .from("MyCustomMethod")
        .toType("Customer")

  @method toType
  @param entityType {String|EntityType} The top level entityType that this query will return.  This method is only needed when a query returns a json result that
  does not include type information.  If the json result consists of more than a simple entity or array of entities, consider using a JsonResultsAdapter instead.
  @return {EntityQuery}
  @chainable
  **/
  proto.toType = function (entityType) {
    assertParam(entityType, "entityType").isString().or().isInstanceOf(EntityType).check();
    return clone(this, "resultEntityType", entityType);
  };
  
  
  /**
  Returns a new query with an added filter criteria; Can be called multiple times which means to 'and' with any existing
  Predicate or can be called with null to clear all predicates.
  @example
      var query = new EntityQuery("Customers")
                .where("CompanyName", "startsWith", "C");
  This can also be expressed using an explicit {{#crossLink "FilterQueryOp"}}{{/crossLink}} as
  @example
      var query = new EntityQuery("Customers")
          .where("CompanyName", FilterQueryOp.StartsWith, "C");
  or a preconstructed {{#crossLink "Predicate"}}{{/crossLink}} may be used
  @example
      var pred = new Predicate("CompanyName", FilterQueryOp.StartsWith, "C");
      var query = new EntityQuery("Customers").where(pred);
  Predicates are often useful when you want to combine multiple conditions in a single filter, such as
  @example
      var pred = Predicate.create("CompanyName", "startswith", "C").and("Region", FilterQueryOp.Equals, null);
      var query = new EntityQuery("Customers")
        .where(pred);
  @example
  More complicated queries can make use of nested property paths
  @example
      var query = new EntityQuery("Products")
        .where("Category.CategoryName", "startswith", "S");
  or OData functions - A list of valid OData functions can be found within the {{#crossLink "Predicate"}}{{/crossLink}} documentation.
  @example
      var query = new EntityQuery("Customers")
        .where("toLower(CompanyName)", "startsWith", "c");
  or to be even more baroque
  @example
      var query = new EntityQuery("Customers")
        .where("toUpper(substring(CompanyName, 1, 2))", FilterQueryOp.Equals, "OM");
  @method where
  @param predicate {Predicate|property|property path, operator, value} Can be either

    - a single {{#crossLink "Predicate"}}{{/crossLink}}

    - or the parameters to create a 'simple' Predicate

    - a property name, a property path with '.' as path seperators or a property expression {String}
    - an operator {FilterQueryOp|String} Either a  {{#crossLink "FilterQueryOp"}}{{/crossLink}} or it's string representation. Case is ignored
    when if a string is provided and any string that matches one of the FilterQueryOp aliases will be accepted.
    - a value {Object} - This will be treated as either a property expression or a literal depending on context.  In general,
    if the value can be interpreted as a property expression it will be, otherwise it will be treated as a literal.
    In most cases this works well, but you can also force the interpretation by making the value argument itself an object with a 'value' property and an 'isLiteral' property set to either true or false.
    Breeze also tries to infer the dataType of any literal based on context, if this fails you can force this inference by making the value argument an object with a 'value' property and a 'dataType'property set
    to one of the breeze.DataType enumeration instances.
    - or a null or undefined ( this causes any existing where clause to be removed)

  @return {EntityQuery}
  @chainable
  **/
  proto.where = function (wherePredicate) {
    if (wherePredicate != null) {
      wherePredicate = Predicate.create(__arraySlice(arguments));
      if (this.fromEntityType) wherePredicate._validate(this.fromEntityType);
      if (this.wherePredicate) {
        wherePredicate = this.wherePredicate.and(wherePredicate);
      }
    }
    return clone(this, "wherePredicate", wherePredicate);
  };

  /**
  Returns a new query that orders the results of the query by property name.  By default sorting occurs is ascending order, but sorting in descending order is supported as well.
  OrderBy clauses may be chained.
  @example
      var query = new EntityQuery("Customers")
        .orderBy("CompanyName");

  or to sort across multiple properties
  @example
      var query = new EntityQuery("Customers")
        .orderBy("Region, CompanyName");

  Nested property paths are also supported
  @example
      var query = new EntityQuery("Products")
        .orderBy("Category.CategoryName");

  Sorting in descending order is supported via the addition of ' desc' to the end of any property path.
  @example
      var query = new EntityQuery("Customers")
        .orderBy("CompanyName desc");

  or
  @example
      var query = new EntityQuery("Customers")
        .orderBy("Region desc, CompanyName desc");
  @method orderBy
  @param propertyPaths {String|Array of String} A comma-separated (',') string of property paths or an array of property paths.
  Each property path can optionally end with " desc" to force a descending sort order. If 'propertyPaths' is either null or omitted then all ordering is removed.
  @param isDescending {Boolean} - If specified, overrides all of the embedded 'desc' tags in the previously specified property paths.
  @return {EntityQuery}
  @chainable
  **/
  proto.orderBy = function (propertyPaths, isDescending) {
    // propertyPaths: can pass in create("A.X,B") or create("A.X desc, B") or create("A.X desc,B", true])
    // isDesc parameter trumps isDesc in propertyName.

    var orderByClause = propertyPaths == null ? null : new OrderByClause(normalizePropertyPaths(propertyPaths), isDescending);
    if (this.orderByClause && orderByClause) {
      orderByClause = new OrderByClause([this.orderByClause, orderByClause]);
    }
    return clone(this, "orderByClause", orderByClause);
  }
  
  /**
  Returns a new query that orders the results of the query by property name in descending order.
  @example
      var query = new EntityQuery("Customers")
        .orderByDesc("CompanyName");

  or to sort across multiple properties
  @example
      var query = new EntityQuery("Customers")
        .orderByDesc("Region, CompanyName");

  Nested property paths are also supported
  @example
      var query = new EntityQuery("Products")
        .orderByDesc("Category.CategoryName");

  @method orderByDesc
  @param propertyPaths {String|Array of String} A comma-separated (',') string of property paths or an array of property paths.
  If 'propertyPaths' is either null or omitted then all ordering is removed.
  @return {EntityQuery}
  @chainable
  **/
  proto.orderByDesc = function (propertyPaths) {
    return this.orderBy(propertyPaths, true);
  };
  
  /**
  Returns a new query that selects a list of properties from the results of the original query and returns the values of just these properties. This
  will be referred to as a projection.
  If the result of this selection "projection" contains entities, these entities will automatically be added to EntityManager's cache and will
  be made 'observable'.
  Any simple properties, i.e. strings, numbers or dates within a projection will not be cached are will NOT be made 'observable'.

  @example
  Simple data properties can be projected
  @example
      var query = new EntityQuery("Customers")
        .where("CompanyName", "startsWith", "C")
        .select("CompanyName");
  This will return an array of objects each with a single "CompanyName" property of type string.
  A similar query could return a navigation property instead
  @example
      var query = new EntityQuery("Customers")
        .where("CompanyName", "startsWith", "C")
        .select("Orders");
  where the result would be an array of objects each with a single "Orders" property that would itself be an array of "Order" entities.
  Composite projections are also possible:
  @example
      var query = new EntityQuery("Customers")
        .where("CompanyName", "startsWith", "C")
        .select("CompanyName, Orders");
  As well as projections involving nested property paths
  @example
      var query = EntityQuery("Orders")
        .where("Customer.CompanyName", "startsWith", "C")
        .select("Customer.CompanyName, Customer, OrderDate");
  @method select
  @param propertyPaths {String|Array of String} A comma-separated (',') string of property paths or an array of property paths.
  If 'propertyPaths' is either null or omitted then any existing projection on the query is removed.
  @return {EntityQuery}
  @chainable
  **/
  proto.select = function (propertyPaths) {
    var selectClause = propertyPaths == null ? null : new SelectClause(normalizePropertyPaths(propertyPaths));
    return clone(this, "selectClause", selectClause);
  };
  
  /**
  Returns a new query that skips the specified number of entities when returning results.
  Any existing 'skip' can be cleared by calling 'skip' with no arguments.
  @example
      var query = new EntityQuery("Customers")
        .where("CompanyName", "startsWith", "C")
        .skip(5);
  @method skip
  @param count {Number} The number of entities to return. If omitted or null any existing skip count on the query is removed.
  @return {EntityQuery}
  @chainable
  **/
  proto.skip = function (count) {
    assertParam(count, "count").isOptional().isNumber().check();
    return clone(this, "skipCount", (count == null) ? null : count);
  };
  
  /**
  Returns a new query that returns only the specified number of entities when returning results. - Same as 'take'.
  Any existing 'top' can be cleared by calling 'top' with no arguments.
  @example
      var query = new EntityQuery("Customers")
        .top(5);
  @method top
  @param count {Number} The number of entities to return.
  If 'count' is either null or omitted then any existing 'top' count on the query is removed.
  @return {EntityQuery}
  @chainable
  **/
  proto.top = function (count) {
    return this.take(count);
  };
  
  /**
  Returns a new query that returns only the specified number of entities when returning results - Same as 'top'.
  Any existing take can be cleared by calling take with no arguments.
  @example
      var query = new EntityQuery("Customers")
        .take(5);
  @method take
  @param count {Number} The number of entities to return.
  If 'count' is either null or omitted then any existing 'take' count on the query is removed.
  @return {EntityQuery}
  @chainable
  **/
  proto.take = function (count) {
    assertParam(count, "count").isOptional().isNumber().check();
    return clone(this, "takeCount", (count == null) ? null : count);
  };
  
  /**
  Returns a new query that will return related entities nested within its results. The expand method allows you to identify related entities, via navigation property
  names such that a graph of entities may be retrieved with a single request. Any filtering occurs before the results are 'expanded'.
  @example
      var query = new EntityQuery("Customers")
        .where("CompanyName", "startsWith", "C")
        .expand("Orders");
  will return the filtered customers each with its "Orders" properties fully resolved.
  Multiple paths may be specified by separating the paths by a ','
  @example
      var query = new EntityQuery("Orders")
        .expand("Customer, Employee")
  and nested property paths my be specified as well
  @example
      var query = new EntityQuery("Orders")
        .expand("Customer, OrderDetails, OrderDetails.Product")
  @method expand
  @param propertyPaths {String|Array of String} A comma-separated list of navigation property names or an array of navigation property names. Each Navigation Property name can be followed
  by a '.' and another navigation property name to enable identifying a multi-level relationship.
  If 'propertyPaths' is either null or omitted then any existing 'expand' clause on the query is removed.
  @return {EntityQuery}
  @chainable
  **/
  proto.expand = function (propertyPaths) {
    var expandClause = propertyPaths == null ? null : new ExpandClause(normalizePropertyPaths(propertyPaths));
    return clone(this, "expandClause", expandClause);
  };
  
  /**
  Returns a new query that includes a collection of parameters to pass to the server.
  @example
      var query = EntityQuery.from("EmployeesFilteredByCountryAndBirthdate")
        .withParameters({ BirthDate: "1/1/1960", Country: "USA" });
   
  will call the 'EmployeesFilteredByCountryAndBirthdate' method on the server and pass in 2 parameters. This
  query will be uri encoded as
  @example
      {serviceApi}/EmployeesFilteredByCountryAndBirthdate?birthDate=1%2F1%2F1960&country=USA

  Parameters may also be mixed in with other query criteria.
  @example
      var query = EntityQuery.from("EmployeesFilteredByCountryAndBirthdate")
        .withParameters({ BirthDate: "1/1/1960", Country: "USA" })
        .where("LastName", "startsWith", "S")
        .orderBy("BirthDate");

  @method withParameters
  @param parameters {Object} A parameters object where the keys are the parameter names and the values are the parameter values.
  @return {EntityQuery}
  @chainable
  **/
  proto.withParameters = function (parameters) {
    assertParam(parameters, "parameters").isObject().check();
    return clone(this, "parameters", parameters);
  };
  
  /**
  Returns a query with the 'inlineCount' capability either enabled or disabled.  With 'inlineCount' enabled, an additional 'inlineCount' property
  will be returned with the query results that will contain the number of entities that would have been returned by this
  query with only the 'where'/'filter' clauses applied, i.e. without any 'skip'/'take' operators applied. For local queries this clause is ignored.

  @example
      var query = new EntityQuery("Customers")
        .take(20)
        .orderBy("CompanyName")
        .inlineCount(true);
  will return the first 20 customers as well as a count of all of the customers in the remote store.

  @method inlineCount
  @param enabled {Boolean=true} Whether or not inlineCount capability should be enabled. If this parameter is omitted, true is assumed.
  @return {EntityQuery}
  @chainable
  **/
  proto.inlineCount = function (enabled) {
    assertParam(enabled, "enabled").isBoolean().isOptional().check();
    enabled = (enabled === undefined) ? true : !!enabled;
    return clone(this, "inlineCountEnabled", enabled);
  };

  proto.useNameOnServer = function(usesNameOnServer) {
    assertParam(usesNameOnServer, "usesNameOnServer").isBoolean().isOptional().check();
    usesNameOnServer = (usesNameOnServer === undefined) ? true : !!usesNameOnServer;
    return clone(this, "usesNameOnServer", usesNameOnServer);
  }
  
  /**
  Returns a query with the 'noTracking' capability either enabled or disabled.  With 'noTracking' enabled, the results of this query
  will not be coerced into entities but will instead look like raw javascript projections. i.e. simple javascript objects.

  @example
      var query = new EntityQuery("Customers")
        .take(20)
        .orderBy("CompanyName")
        .noTracking(true);

  @method noTracking
  @param enabled {Boolean=true} Whether or not the noTracking capability should be enabled. If this parameter is omitted, true is assumed.
  @return {EntityQuery}
  @chainable
  **/
  proto.noTracking = function (enabled) {
    assertParam(enabled, "enabled").isBoolean().isOptional().check();
    enabled = (enabled === undefined) ? true : !!enabled;
    return clone(this, "noTrackingEnabled", enabled);
  };
  
  /**
  Returns a copy of this EntityQuery with the specified {{#crossLink "EntityManager"}}{{/crossLink}}, {{#crossLink "DataService"}}{{/crossLink}},
  {{#crossLink "JsonResultsAdapter"}}{{/crossLink}}, {{#crossLink "MergeStrategy"}}{{/crossLink}} or {{#crossLink "FetchStrategy"}}{{/crossLink}} applied.
  @example
      // 'using' can be used to return a new query with a specified EntityManager.
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders")
        .using(em);
  or with a specified {{#crossLink "MergeStrategy"}}{{/crossLink}}
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders")
        .using(MergeStrategy.PreserveChanges);
  or with a specified {{#crossLink "FetchStrategy"}}{{/crossLink}}
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders")
        .using(FetchStrategy.FromLocalCache);
  
  @method using
  @param obj {EntityManager|QueryOptions|DataService|MergeStrategy|FetchStrategy|JsonResultsAdapter|config object} The object to update in creating a new EntityQuery from an existing one.
  @return {EntityQuery}
  @chainable
  **/
  proto.using = function (obj) {
    if (!obj) return this;
    var eq = clone(this);
    processUsing(eq, {
      entityManager: null,
      dataService: null,
      queryOptions: null,
      fetchStrategy: function (eq, val) {
        eq.queryOptions = (eq.queryOptions || new QueryOptions()).using(val)
      },
      mergeStrategy: function (eq, val) {
        eq.queryOptions = (eq.queryOptions || new QueryOptions()).using(val)
      },
      jsonResultsAdapter: function (eq, val) {
        eq.dataService = (eq.dataService || new DataService()).using({ jsonResultsAdapter: val })
      }
    }, obj);
    return eq;
  };
  
  /**
  Executes this query.  This method requires that an EntityManager has been previously specified via the "using" method.
  @example
  This method can be called using a 'promises' syntax ( recommended)
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders").using(em);
      query.execute().then( function(data) {
          ... query results processed here
      }).fail( function(err) {
          ... query failure processed here
      });
  or with callbacks
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders").using(em);
      query.execute(
        function(data) {
                    var orders = data.results;
                    ... query results processed here
                },
        function(err) {
                    ... query failure processed here
                });
  Either way this method is the same as calling the EntityManager 'execute' method.
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders");
      em.executeQuery(query).then( function(data) {
         var orders = data.results;
          ... query results processed here
      }).fail( function(err) {
         ... query failure processed here
      });

  @method execute
  @async

  @param callback {Function} Function called on success.

  successFunction([data])
  @param [callback.data] {Object}
  @param callback.data.results {Array of Entity}
  @param callback.data.query {EntityQuery} The original query
  @param callback.data.httpResponse {HttpResponse} The HttpResponse returned from the server.
  @param callback.data.inlineCount {Integer} Only available if 'inlineCount(true)' was applied to the query.  Returns the count of
  items that would have been returned by the query before applying any skip or take operators, but after any filter/where predicates
  would have been applied.
  @param callback.data.retrievedEntities {Array of Entity} All entities returned by the query.  Differs from results when .expand() is used.

  @param errorCallback {Function} Function called on failure.

  failureFunction([error])
  @param [errorCallback.error] {Error} Any error that occured wrapped into an Error object.
  @param [errorCallback.error.query] The query that caused the error.
  @param [errorCallback.error.httpResponse] {HttpResponse} The raw XMLHttpRequest returned from the server.

  @return {Promise}
  **/
  proto.execute = function (callback, errorCallback) {
    if (!this.entityManager) {
      throw new Error("An EntityQuery must have its EntityManager property set before calling 'execute'");
    }
    return this.entityManager.executeQuery(this, callback, errorCallback);
  };
  
  /**
  Executes this query against the local cache.  This method requires that an EntityManager have been previously specified via the "using" method.
  @example
      // assume em is an entityManager already filled with order entities;
      var query = new EntityQuery("Orders").using(em);
      var orders = query.executeLocally();

  Note that calling this method is the same as calling {{#crossLink "EntityManager/executeQueryLocally"}}{{/crossLink}}.

  @method executeLocally
  **/
  proto.executeLocally = function () {
    if (!this.entityManager) {
      throw new Error("An EntityQuery must have its EntityManager property set before calling 'executeLocally'");
    }
    return this.entityManager.executeQueryLocally(this);
  };
  
  proto.toJSON = function () {
    return this.toJSONExt();
  }
  
  proto.toJSONExt = function (context) {
    context = context || {};
    context.entityType = context.entityType || this.fromEntityType;
    context.propertyPathFn = context.toNameOnServer ? context.entityType.clientPropertyPathToServer.bind(context.entityType) : __identity;
    
    var that = this;
    
    var toJSONExtFn = function (v) {
      return v ? v.toJSONExt(context) : undefined;
    };
    return __toJson(this, {
      "from,resourceName": null,
      "toType,resultEntityType": function (v) {
        // resultEntityType can be either a string or an entityType
        return v ? (__isString(v) ? v : v.name) : undefined;
      },
      "where,wherePredicate": toJSONExtFn,
      "orderBy,orderByClause": toJSONExtFn,
      "select,selectClause": toJSONExtFn,
      "expand,expandClause": toJSONExtFn,
      "skip,skipCount": null,
      "take,takeCount": null,
      parameters: function (v) {
        return __isEmpty(v) ? undefined : v;
      },
      "inlineCount,inlineCountEnabled": false,
      "noTracking,noTrackingEnabled": false,
      queryOptions: null
    });

  }
  
  function fromJSON(eq, json) {
    __toJson(json, {
      "resourceName,from": null,
      // just the name comes back and will be resolved later
      "resultEntityType,toType": null,
      "wherePredicate,where": function (v) {
        return v ? new Predicate(v) : undefined;
      },
      "orderByClause,orderBy": function (v) {
        return v ? new OrderByClause(v) : undefined;
      },
      "selectClause,select": function (v) {
        return v ? new SelectClause(v) : undefined;
      },
      "expandClause,expand": function (v) {
        return v ? new ExpandClause(v) : undefined;
      },
      "skipCount,skip": null,
      "takeCount,take": null,
      parameters: function (v) {
        return __isEmpty(v) ? undefined : v;
      },
      "inlineCountEnabled,inlineCount": false,
      "noTrackingEnabled,noTracking": false,
      queryOptions: function (v) {
        return v ? QueryOptions.fromJSON(v) : undefined;
      }
    }, eq);
    return eq;
  }
  
  /**
  Static method that creates an EntityQuery that will allow 'requerying' an entity or a collection of entities by primary key. This can be useful
  to force a requery of selected entities, or to restrict an existing collection of entities according to some filter.

  Works for a single entity or an array of entities of the SAME type.
  Does not work for an array of entities of different types.

  @example
      // assuming 'customers' is an array of 'Customer' entities retrieved earlier.
      var customersQuery = EntityQuery.fromEntities(customers);
  The resulting query can, of course, be extended
  @example
      // assuming 'customers' is an array of 'Customer' entities retrieved earlier.
      var customersQuery = EntityQuery.fromEntities(customers)
        .where("Region", FilterQueryOp.NotEquals, null);
  Single entities can requeried as well.
  @example
      // assuming 'customer' is a 'Customer' entity retrieved earlier.
      var customerQuery = EntityQuery.fromEntities(customer);
  will create a query that will return an array containing a single customer entity.
  @method fromEntities
  @static
  @param entities {Entity|Array of Entity} The entities for which we want to create an EntityQuery.
  @return {EntityQuery}
  @chainable
  **/
  ctor.fromEntities = function (entities) {
    assertParam(entities, "entities").isEntity().or().isNonEmptyArray().isEntity().check();
    if (!Array.isArray(entities)) {
      entities = __arraySlice(arguments);
    }
    var firstEntity = entities[0];
    var type = firstEntity.entityType;
    if (entities.some(function(e){
      return e.entityType !== type;
    })) {
      throw new Error("All 'fromEntities' must be the same type; at least one is not of type " +
        type.name);
    }
    var q = new EntityQuery(type.defaultResourceName);
    var preds = entities.map(function (entity) {
      return buildPredicate(entity);
    });
    var pred = Predicate.or(preds);
    q = q.where(pred);
    var em = firstEntity.entityAspect.entityManager;
    if (em) {
      q = q.using(em);
    }
    return q;
  };
  
  /**
  Creates an EntityQuery for the specified {{#crossLink "EntityKey"}}{{/crossLink}}.
  @example
      var empType = metadataStore.getEntityType("Employee");
      var entityKey = new EntityKey(empType, 1);
      var query = EntityQuery.fromEntityKey(entityKey);
  or
  @example
      // 'employee' is a previously queried employee
      var entityKey = employee.entityAspect.getKey();
      var query = EntityQuery.fromEntityKey(entityKey);
  @method fromEntityKey
  @static
  @param entityKey {EntityKey} The {{#crossLink "EntityKey"}}{{/crossLink}} for which a query will be created.
  @return {EntityQuery}
  @chainable
  **/
  ctor.fromEntityKey = function (entityKey) {
    assertParam(entityKey, "entityKey").isInstanceOf(EntityKey).check();
    var q = new EntityQuery(entityKey.entityType.defaultResourceName);
    var pred = buildKeyPredicate(entityKey);
    q = q.where(pred).toType(entityKey.entityType);
    return q;
  };
  
  /**
  Creates an EntityQuery for the specified entity and {{#crossLink "NavigationProperty"}}{{/crossLink}}.
  @example
      // 'employee' is a previously queried employee
      var ordersNavProp = employee.entityType.getProperty("Orders");
      var query = EntityQuery.fromEntityNavigation(employee, ordersNavProp);
  will return a query for the "Orders" of the specified 'employee'.
  @method fromEntityNavigation
  @static
  @param entity {Entity} The Entity whose navigation property will be queried.
  @param navigationProperty {NavigationProperty|String} The {{#crossLink "NavigationProperty"}}{{/crossLink}} or name of the NavigationProperty to be queried.
  @return {EntityQuery}
  @chainable
  **/
  ctor.fromEntityNavigation = function (entity, navigationProperty) {
    assertParam(entity, "entity").isEntity().check();
    var navProperty = entity.entityType._checkNavProperty(navigationProperty);
    var q = new EntityQuery(navProperty.entityType.defaultResourceName);
    var pred = buildNavigationPredicate(entity, navProperty);
    q = q.where(pred);
    var em = entity.entityAspect.entityManager;
    return em ? q.using(em) : q;
  };
  
  // protected methods
  
  proto._getFromEntityType = function (metadataStore, throwErrorIfNotFound) {
    // Uncomment next two lines if we make this method public.
    // assertParam(metadataStore, "metadataStore").isInstanceOf(MetadataStore).check();
    // assertParam(throwErrorIfNotFound, "throwErrorIfNotFound").isBoolean().isOptional().check();
    var entityType = this.fromEntityType;
    if (entityType) return entityType;
    
    var resourceName = this.resourceName;
    if (!resourceName) {
      throw new Error("There is no resourceName for this query");
    }
    
    if (metadataStore.isEmpty()) {
      if (throwErrorIfNotFound) {
        throw new Error("There is no metadata available for this query. " +
            "Are you querying the local cache before you've fetched metadata?");
      } else {
        return null;
      }
    }
    
    var entityTypeName = metadataStore.getEntityTypeNameForResourceName(resourceName);
    if (entityTypeName) {
      entityType = metadataStore._getEntityType(entityTypeName);
    } else {
      entityType = this._getToEntityType(metadataStore, true);
    }
    
    if (!entityType) {
      if (throwErrorIfNotFound) {
        throw new Error(__formatString("Cannot find an entityType for resourceName: '%1'. " 
            + " Consider adding an 'EntityQuery.toType' call to your query or " 
            + "calling the MetadataStore.setEntityTypeForResourceName method to register an entityType for this resourceName.", resourceName));
      } else {
        return null;
      }
    }
    
    this.fromEntityType = entityType;
    return entityType;

  };
  
  proto._getToEntityType = function (metadataStore, skipFromCheck) {
    // skipFromCheck is to avoid recursion if called from _getFromEntityType;
    if (this.resultEntityType instanceof EntityType) {
      return this.resultEntityType;
    } else if (this.resultEntityType) {
      // resultEntityType is a string
      this.resultEntityType = metadataStore._getEntityType(this.resultEntityType, false);
      return this.resultEntityType;
    } else {
      // resolve it, if possible, via the resourceName
      // do not cache this value in this case
      // cannot determine the resultEntityType if a selectClause is present.
      return skipFromCheck ? null : (!this.selectClause) && this._getFromEntityType(metadataStore, false);
    }
  };
  
  // for testing
  proto._toUri = function (em) {
    var ds = DataService.resolve([em.dataService]);
    return ds.uriBuilder.buildUri(this, em.metadataStore);
  }
  
  // private functions
  
  function clone(that, propName, value) {
    // immutable queries mean that we don't need to clone if no change in value.
    if (propName) {
      if (that[propName] === value) return that;
    }
    // copying QueryOptions is safe because they are are immutable;
    var copy = __extend(new EntityQuery(), that, [
      "resourceName",
      "fromEntityType",
      "wherePredicate",
      "orderByClause",
      "selectClause",
      "skipCount",
      "takeCount",
      "expandClause",
      "inlineCountEnabled",
      "noTrackingEnabled",
      "usesNameOnServer",
      "queryOptions",
      "entityManager",
      "dataService",
      "resultEntityType"
    ]);
    copy.parameters = __extend({}, that.parameters);
    if (propName) {
      copy[propName] = value;
    }
    return copy;
  }
  
  function processUsing(eq, map, value, propertyName) {
    var typeName = value._$typeName || (value.parentEnum && value.parentEnum.name);
    var key = typeName && typeName.substr(0, 1).toLowerCase() + typeName.substr(1);
    if (propertyName && key != propertyName) {
      throw new Error("Invalid value for property: " + propertyName);
    }
    if (key) {
      var fn = map[key];
      if (fn === undefined) {
        throw new Error("Invalid config property: " + key);
      } else if (fn === null) {
        eq[key] = value;
      } else {
        fn(eq, value);
      }
    } else {
      __objectForEach(value, function (propName, val) {
        processUsing(eq, map, val, propName)
      });
    }
  }
  
  function normalizePropertyPaths(propertyPaths) {
    assertParam(propertyPaths, "propertyPaths").isOptional().isString().or().isArray().isString().check();
    if (typeof propertyPaths === 'string') {
      propertyPaths = propertyPaths.split(",");
    }
    
    propertyPaths = propertyPaths.map(function (pp) {
      return pp.trim();
    });
    return propertyPaths;
  }
  
  function buildPredicate(entity) {
    var entityType = entity.entityType;
    var predParts = entityType.keyProperties.map(function (kp) {
      return Predicate.create(kp.name, FilterQueryOp.Equals, entity.getProperty(kp.name));
    });
    var pred = Predicate.and(predParts);
    return pred;
  }
  
  function buildKeyPredicate(entityKey) {
    var keyProps = entityKey.entityType.keyProperties;
    var preds = __arrayZip(keyProps, entityKey.values, function (kp, v) {
      return Predicate.create(kp.name, FilterQueryOp.Equals, v);
    });
    var pred = Predicate.and(preds);
    return pred;
  }
  
  function buildNavigationPredicate(entity, navigationProperty) {
    if (navigationProperty.isScalar) {
      if (navigationProperty.foreignKeyNames.length === 0) return null;
      var relatedKeyValues = navigationProperty.foreignKeyNames.map(function (fkName) {
        return entity.getProperty(fkName);
      });
      var entityKey = new EntityKey(navigationProperty.entityType, relatedKeyValues);
      return buildKeyPredicate(entityKey);
    } else {
      var inverseNp = navigationProperty.getInverse();
      var foreignKeyNames = inverseNp ? inverseNp.foreignKeyNames : navigationProperty.invForeignKeyNames;
      if (foreignKeyNames.length === 0) return null;
      var keyValues = entity.entityAspect.getKey().values;
      var predParts = __arrayZip(foreignKeyNames, keyValues, function (fkName, kv) {
        return Predicate.create(fkName, FilterQueryOp.Equals, kv);
      });
      return Predicate.and(predParts);
    }
  }
  
  return ctor;
})();

var FilterQueryOp = (function () {
  /**
   FilterQueryOp is an 'Enum' containing all of the valid  {{#crossLink "Predicate"}}{{/crossLink}}
   filter operators for an {{#crossLink "EntityQuery"}}{{/crossLink}}.

   @class FilterQueryOp
   @static
   **/
  var aEnum = new Enum("FilterQueryOp");
  /**
   Aliases: "eq", "=="
   @property Equals {FilterQueryOp}
   @final
   @static
   **/
  aEnum.Equals = aEnum.addSymbol({ operator: "eq" });
  /**
   Aliases: "ne", "!="
   @property NotEquals {FilterQueryOp}
   @final
   @static
   **/
  aEnum.NotEquals = aEnum.addSymbol({ operator: "ne" });
  /**
   Aliases: "gt", ">"
   @property GreaterThan {FilterQueryOp}
   @final
   @static
   **/
  aEnum.GreaterThan = aEnum.addSymbol({ operator: "gt" });
  /**
   Aliases: "lt", "<"
   @property LessThan {FilterQueryOp}
   @final
   @static
   **/
  aEnum.LessThan = aEnum.addSymbol({ operator: "lt" });
  /**
   Aliases: "ge", ">="
   @property GreaterThanOrEqual {FilterQueryOp}
   @final
   @static
   **/
  aEnum.GreaterThanOrEqual = aEnum.addSymbol({ operator: "ge" });
  /**
   Aliases: "le", "<="
   @property LessThanOrEqual {FilterQueryOp}
   @final
   @static
   **/
  aEnum.LessThanOrEqual = aEnum.addSymbol({ operator: "le" });
  /**
   String operation: Is a string a substring of another string.
   Aliases: "substringof"
   @property Contains {FilterQueryOp}
   @final
   @static
   **/
  aEnum.Contains = aEnum.addSymbol({ operator: "contains" });
  /**
   @property StartsWith {FilterQueryOp}
   @final
   @static
   **/
  aEnum.StartsWith = aEnum.addSymbol({ operator: "startswith" });
  /**
   @property EndsWith {FilterQueryOp}
   @final
   @static
   **/
  aEnum.EndsWith = aEnum.addSymbol({ operator: "endswith" });
  
  /**
   Aliases: "some"
   @property Any {FilterQueryOp}
   @final
   @static
   **/
  aEnum.Any = aEnum.addSymbol({ operator: "any" });
  
  /**
   Aliases: "every"
   @property All {FilterQueryOp}
   @final
   @static
   **/
  aEnum.All = aEnum.addSymbol({ operator: "all" });

  /**
   @property In {FilterQueryOp}
   @final
   @static
   **/
  aEnum.In = aEnum.addSymbol({ operator: "in" });

  /**
   @property IsTypeOf {FilterQueryOp}
   @final
   @static
   **/
  aEnum.IsTypeOf = aEnum.addSymbol({ operator: "isof" });
  
  aEnum.resolveSymbols();

  return aEnum;
})();

var BooleanQueryOp = (function () {
  var aEnum = new Enum("BooleanQueryOp");
  aEnum.And = aEnum.addSymbol({ operator: "and" });
  aEnum.Or = aEnum.addSymbol({ operator: "or" });
  aEnum.Not = aEnum.addSymbol({ operator: "not" });
  
  aEnum.resolveSymbols();

  return aEnum;
})();

/*
 An OrderByClause is a description of the properties and direction that the result
 of a query should be sorted in.  OrderByClauses are immutable, which means that any
 method that would modify an OrderByClause actually returns a new OrderByClause.

 For example for an Employee object with properties of 'Company' and 'LastName' the following would be valid expressions:

 var obc = new OrderByClause("Company.CompanyName, LastName")
 or
 var obc = new OrderByClause("Company.CompanyName desc, LastName")
 or
 var obc = new OrderByClause("Company.CompanyName, LastName", true);
 @class OrderByClause
 */
var OrderByClause = (function () {
  
  var ctor = function (propertyPaths, isDesc) {

    if (propertyPaths.length > 1) {
      // you can also pass in an array of orderByClauses
      if (propertyPaths[0] instanceof OrderByClause) {
        this.items = Array.prototype.concat.apply(propertyPaths[0].items, propertyPaths.slice(1).map(__pluck("items")) );
        return;
      }
      var items = propertyPaths.map(function (pp) {
        return new OrderByItem(pp, isDesc);
      });
    } else {
      var items = [new OrderByItem(propertyPaths[0], isDesc)];
    }
    this.items = items;
  };
  var proto = ctor.prototype;
  
  proto.validate = function (entityType) {
    if (entityType == null || entityType.isAnonymous) return;
    this.items.forEach(function (item) {
      item.validate(entityType)
    });
  };


  
  proto.getComparer = function (entityType) {
    var orderByFuncs = this.items.map(function (obc) {
      return obc.getComparer(entityType);
    });
    return function (entity1, entity2) {
      for (var i = 0; i < orderByFuncs.length; i++) {
        var result = orderByFuncs[i](entity1, entity2);
        if (result !== 0) {
          return result;
        }
      }
      return 0;
    };
  };
  
  proto.toJSONExt = function (context) {
    return this.items.map(function (item) {
      return context.propertyPathFn(item.propertyPath) + (item.isDesc ? " desc" : "");
    });
  };
  
  var OrderByItem = function (propertyPath, isDesc) {
    if (!(typeof propertyPath === 'string')) {
      throw new Error("propertyPath is not a string");
    }
    propertyPath = propertyPath.trim();
    
    var parts = propertyPath.split(' ');
    // parts[0] is the propertyPath; [1] would be whether descending or not.
    if (parts.length > 1 && isDesc !== true && isDesc !== false) {
      isDesc = __stringStartsWith(parts[1].toLowerCase(), "desc");
      if (!isDesc) {
        // isDesc is false but check to make sure its intended.
        var isAsc = __stringStartsWith(parts[1].toLowerCase(), "asc");
        if (!isAsc) {
          throw new Error("the second word in the propertyPath must begin with 'desc' or 'asc'");
        }

      }
    }
    this.propertyPath = parts[0];
    this.isDesc = isDesc;
  };
  
  var itemProto = OrderByItem.prototype;
  
  itemProto.validate = function (entityType) {
    if (entityType == null || entityType.isAnonymous) return;
    // will throw an exception on bad propertyPath
    this.lastProperty = entityType.getProperty(this.propertyPath, true);
  };
  
  itemProto.getComparer = function (entityType) {
    if (!this.lastProperty) this.validate(entityType);
    if (this.lastProperty) {
      var propDataType = this.lastProperty.dataType;
      var isCaseSensitive = this.lastProperty.parentType.metadataStore.localQueryComparisonOptions.isCaseSensitive;
    }
    var propertyPath = this.propertyPath;
    var isDesc = this.isDesc;
    
    return function (entity1, entity2) {
      var value1 = getPropertyPathValue(entity1, propertyPath);
      var value2 = getPropertyPathValue(entity2, propertyPath);
      var dataType = propDataType || (value1 && DataType.fromValue(value1)) || DataType.fromValue(value2);
      if (dataType === DataType.String) {
        if (isCaseSensitive) {
          value1 = value1 || "";
          value2 = value2 || "";
        } else {
          value1 = (value1 || "").toLowerCase();
          value2 = (value2 || "").toLowerCase();
        }
      } else {
        var normalize = DataType.getComparableFn(dataType);
        value1 = normalize(value1);
        value2 = normalize(value2);
      }
      if (value1 === value2) {
        return 0;
      } else if (value1 > value2 || value2 === undefined) {
        return isDesc ? -1 : 1;
      } else {
        return isDesc ? 1 : -1;
      }
    };
  };
  
  return ctor;
})();

// Not exposed
var SelectClause = (function () {
  
  var ctor = function (propertyPaths) {
    this.propertyPaths = propertyPaths;
    this._pathNames = propertyPaths.map(function (pp) {
      return pp.replace(".", "_");
    });
  };
  var proto = ctor.prototype;
  
  proto.validate = function (entityType) {
    if (entityType == null || entityType.isAnonymous) return; // can't validate yet
    // will throw an exception on bad propertyPath
    this.propertyPaths.forEach(function (path) {
      entityType.getProperty(path, true);
    });
  };
  
  proto.toFunction = function (/* config */) {
    var that = this;
    return function (entity) {
      var result = {};
      that.propertyPaths.forEach(function (path, i) {
        result[that._pathNames[i]] = getPropertyPathValue(entity, path);
      });
      return result;
    };
  };
  
  proto.toJSONExt = function (context) {
    return this.propertyPaths.map(function (pp) {
      return context.propertyPathFn(pp);
    })
  };
  
  return ctor;
})();

// Not exposed
var ExpandClause = (function () {
  
  // propertyPaths is an array of strings.
  var ctor = function (propertyPaths) {
    this.propertyPaths = propertyPaths;
  };
  var proto = ctor.prototype;
  
  proto.toJSONExt = function (context) {
    return this.propertyPaths.map(function (pp) {
      return context.propertyPathFn(pp);
    })
  };
  
  return ctor;
})();

// used by EntityQuery and Predicate
function getPropertyPathValue(obj, propertyPath) {
  var properties = Array.isArray(propertyPath) ? propertyPath : propertyPath.split(".");
  if (properties.length === 1) {
    return obj.getProperty(propertyPath);
  } else {
    var nextValue = obj;
    // hack use of some to perform mapFirst operation.
    properties.some(function (prop) {
      nextValue = nextValue.getProperty(prop);
      return nextValue == null;
    });
    return nextValue;
  }
}

// expose
breeze.FilterQueryOp = FilterQueryOp;
breeze.EntityQuery = EntityQuery;

// Not documented - only exposed for testing purposes
breeze.OrderByClause = OrderByClause;

;/**
@module breeze
**/

var MergeStrategy = (function () {
  /**
  MergeStrategy is an 'Enum' that determines how entities are merged into an EntityManager.

  @class MergeStrategy
  @static
  **/
  var MergeStrategy = new Enum("MergeStrategy");
  /**
  MergeStrategy.PreserveChanges updates the cached entity with the incoming values unless the cached entity is in a changed
  state (added, modified, deleted) in which case the incoming values are ignored. The updated cached entity’s EntityState will
  remain {{#crossLink "EntityState/Unchanged"}}{{/crossLink}} unless you’re importing entities in which case the new EntityState will
  be that of the imported entities.

  @property PreserveChanges {MergeStrategy}
  @final
  @static
  **/
  MergeStrategy.PreserveChanges = MergeStrategy.addSymbol();
  /**
  MergeStrategy.OverwriteChanges always updates the cached entity with incoming values even if the entity is in
  a changed state (added, modified, deleted). After the merge, the pending changes are lost.
  The new EntityState will be  {{#crossLink "EntityState/Unchanged"}}{{/crossLink}} unless you’re importing entities
  in which case the new EntityState will be that of the imported entities.

  @property OverwriteChanges {MergeStrategy}
  @final
  @static
  **/
  MergeStrategy.OverwriteChanges = MergeStrategy.addSymbol();

  /**
  SkipMerge is used to ignore incoming values. Adds the incoming entity to the cache only if there is no cached entity with the same key.
  This is the fastest merge strategy but your existing cached data will remain “stale”.

  @property SkipMerge {MergeStrategy}
  @final
  @static
  **/
  MergeStrategy.SkipMerge = MergeStrategy.addSymbol();

  /**
  Disallowed is used to throw an exception if there is an incoming entity with the same key as an entity already in the cache.
  Use this strategy when you want to be sure that the incoming entity is not already in cache.
  This is the default strategy for EntityManager.attachEntity.

  @property Disallowed {MergeStrategy}
  @final
  @static
  **/
  MergeStrategy.Disallowed = MergeStrategy.addSymbol();
  MergeStrategy.resolveSymbols();
  return MergeStrategy;
})();

var FetchStrategy = (function () {
  /**
  FetchStrategy is an 'Enum' that determines how and where entities are retrieved from as a result of a query.

  @class FetchStrategy
  @static
  **/
  var FetchStrategy = new Enum("FetchStrategy");
  /**
  FromServer is used to tell the query to execute the query against a remote data source on the server.
  @property FromServer {MergeStrategy}
  @final
  @static
  **/
  FetchStrategy.FromServer = FetchStrategy.addSymbol();
  /**
  FromLocalCache is used to tell the query to execute the query against a local EntityManager instead of going to a remote server.
  @property FromLocalCache {MergeStrategy}
  @final
  @static
  **/
  FetchStrategy.FromLocalCache = FetchStrategy.addSymbol();
  FetchStrategy.resolveSymbols();
  return FetchStrategy;
})();

var QueryOptions = (function () {
  /**
  A QueryOptions instance is used to specify the 'options' under which a query will occur.

  @class QueryOptions
  **/

  /**
  QueryOptions constructor
  @example
      var newQo = new QueryOptions( { mergeStrategy: MergeStrategy.OverwriteChanges });
      // assume em1 is a preexisting EntityManager
      em1.setProperties( { queryOptions: newQo });

  Any QueryOptions property that is not defined will be defaulted from any QueryOptions defined at a higher level in the breeze hierarchy, i.e.
  -  from query.queryOptions
  -  to   entityManager.queryOptions
  -  to   QueryOptions.defaultInstance;

  @method <ctor> QueryOptions
  @param [config] {Object}
  @param [config.fetchStrategy] {FetchStrategy}
  @param [config.mergeStrategy] {MergeStrategy}
  @param [config.includeDeleted] {Boolean} Whether query should return cached deleted entities (false by default)
  **/
  var ctor = function QueryOptions(config) {
    updateWithConfig(this, config);
  };
  var proto = ctor.prototype;
  proto._$typeName = "QueryOptions";

  /**
  A {{#crossLink "FetchStrategy"}}{{/crossLink}}
  __readOnly__
  @property fetchStrategy {FetchStrategy}
  **/

  /**
  A {{#crossLink "MergeStrategy"}}{{/crossLink}}
  __readOnly__
  @property mergeStrategy {MergeStrategy}
  **/

  /**
  Whether to include cached deleted entities in a query result (false by default).

  __readOnly__
  @property includeDeleted {Boolean}
  **/

  ctor.resolve = function (queryOptionsArray) {
    return new QueryOptions(__resolveProperties(queryOptionsArray, ["fetchStrategy", "mergeStrategy", "includeDeleted"]));
  };

  /**
  The default value whenever QueryOptions are not specified.
  @property defaultInstance {QueryOptions}
  @static
  **/
  ctor.defaultInstance = new ctor({
    fetchStrategy: FetchStrategy.FromServer,
    mergeStrategy: MergeStrategy.PreserveChanges,
    includeDeleted: false
  });

  /**
  Returns a copy of this QueryOptions with the specified {{#crossLink "MergeStrategy"}}{{/crossLink}},
  {{#crossLink "FetchStrategy"}}{{/crossLink}}, or 'includeDeleted' option applied.
  @example
      // Given an EntityManager instance, em
      var queryOptions = em.queryOptions.using(MergeStrategy.PreserveChanges);
  or
  @example
      var queryOptions = em.queryOptions.using(FetchStrategy.FromLocalCache);
  or
  @example
      var queryOptions = em.queryOptions.using({ mergeStrategy: MergeStrategy.OverwriteChanges });
  or
  @example
      var queryOptions = em.queryOptions.using({
          includeDeleted: true,
          fetchStrategy:  FetchStrategy.FromLocalCache 
      });
  @method using
  @param config {Configuration Object|MergeStrategy|FetchStrategy} The object to apply to create a new QueryOptions.
  @return {QueryOptions}
  @chainable
  **/
  proto.using = function (config) {
    if (!config) return this;
    var result = new QueryOptions(this);
    if (MergeStrategy.contains(config)) {
      config = { mergeStrategy: config };
    } else if (FetchStrategy.contains(config)) {
      config = { fetchStrategy: config };
    }
    return updateWithConfig(result, config);
  };

  /**
  Sets the 'defaultInstance' by creating a copy of the current 'defaultInstance' and then applying all of the properties of the current instance.
  The current instance is returned unchanged.
  @method setAsDefault
  @example
      var newQo = new QueryOptions( { mergeStrategy: MergeStrategy.OverwriteChanges });
      newQo.setAsDefault();
  @chainable
  **/
  proto.setAsDefault = function () {
    return __setAsDefault(this, ctor);
  };

  proto.toJSON = function () {
    return __toJson(this, {
      fetchStrategy: null,
      mergeStrategy: null,
      includeDeleted: false
    });
  };

  ctor.fromJSON = function (json) {
    return new QueryOptions({
      fetchStrategy: FetchStrategy.fromName(json.fetchStrategy),
      mergeStrategy: MergeStrategy.fromName(json.mergeStrategy),
      includeDeleted: json.includeDeleted === true
    });
  };

  function updateWithConfig(obj, config) {
    if (config) {
      assertConfig(config)
          .whereParam("fetchStrategy").isEnumOf(FetchStrategy).isOptional()
          .whereParam("mergeStrategy").isEnumOf(MergeStrategy).isOptional()
          .whereParam("includeDeleted").isBoolean().isOptional()
          .applyAll(obj);
    }
    return obj;
  }

  return ctor;
})();

breeze.QueryOptions = QueryOptions;
breeze.FetchStrategy = FetchStrategy;
breeze.MergeStrategy = MergeStrategy;


;/**
 @module breeze
 **/

var EntityGroup = (function () {

  var ctor = function EntityGroup(entityManager, entityType) {
    this.entityManager = entityManager;
    this.entityType = entityType;
    // freeze the entityType after the first instance of this type is either created or queried.
    this.entityType.isFrozen = true;
    this._indexMap = {};
    this._entities = [];
    this._emptyIndexes = [];
  };
  var proto = ctor.prototype;

  proto.attachEntity = function (entity, entityState, mergeStrategy) {
    // entity should already have an aspect.
    var aspect = entity.entityAspect;

    if (!aspect._initialized) {
      this.entityType._initializeInstance(entity);
    }
    delete aspect._initialized;

    var keyInGroup = aspect.getKey()._keyInGroup;
    var ix = this._indexMap[keyInGroup];
    if (ix >= 0) {
      var targetEntity = this._entities[ix];
      var targetEntityState = targetEntity.entityAspect.entityState;
      var wasUnchanged = targetEntityState.isUnchanged();
      if (targetEntity === entity) {
        aspect.entityState = entityState;
      } else if (mergeStrategy === MergeStrategy.Disallowed) {
        throw new Error("A MergeStrategy of 'Disallowed' does not allow you to attach an entity when an entity with the same key is already attached: " + aspect.getKey());
      } else if (mergeStrategy === MergeStrategy.OverwriteChanges || (mergeStrategy === MergeStrategy.PreserveChanges && wasUnchanged)) {
        // unwrapInstance returns an entity with server side property names - so we need to use DataProperty.getRawValueFromServer these when we apply
        // the property values back to the target.
        var rawServerEntity = this.entityManager.helper.unwrapInstance(entity);
        this.entityType._updateTargetFromRaw(targetEntity, rawServerEntity, DataProperty.getRawValueFromServer);
        targetEntity.entityAspect.setEntityState(entityState);
      }
      return targetEntity;
    } else {
      if (this._emptyIndexes.length === 0) {
        ix = this._entities.push(entity) - 1;
      } else {
        ix = this._emptyIndexes.pop();
        this._entities[ix] = entity;
      }
      this._indexMap[keyInGroup] = ix;
      aspect.entityState = entityState;
      aspect.entityGroup = this;
      aspect.entityManager = this.entityManager;
      return entity;
    }
  };

  proto.detachEntity = function (entity) {
    // by this point we have already determined that this entity
    // belongs to this group.
    var aspect = entity.entityAspect;
    var keyInGroup = aspect.getKey()._keyInGroup;
    var ix = this._indexMap[keyInGroup];
    if (ix === undefined) {
      // shouldn't happen.
      throw new Error("internal error - entity cannot be found in group");
    }
    delete this._indexMap[keyInGroup];
    this._emptyIndexes.push(ix);
    this._entities[ix] = null;
    return entity;
  };


  // returns entity based on an entity key defined either as an array of key values or an EntityKey
  proto.findEntityByKey = function (entityKey) {
    var keyInGroup;
    if (entityKey instanceof EntityKey) {
      keyInGroup = entityKey._keyInGroup;
    } else {
      keyInGroup = EntityKey.createKeyString(entityKey);
    }
    var ix = this._indexMap[keyInGroup];
    // can't use just (ix) below because 0 is valid
    return (ix !== undefined) ? this._entities[ix] : null;
  };

  proto.hasChanges = function () {
    var entities = this._entities;
    var unchanged = EntityState.Unchanged;
    for (var i = 0, len = entities.length; i < len; i++){
      var e = entities[i];
      if (e && e.entityAspect.entityState !== unchanged){
        return true;
      }
    }
    return false;
  };

  proto.getChanges = function () {
    var entities = this._entities;
    var unchanged = EntityState.Unchanged;
    var changes = [];
    for (var i = 0, len = entities.length; i < len; i++){
      var e = entities[i];
      if (e && e.entityAspect.entityState !== unchanged){
        changes.push(e);
      }
    }
    return changes;
  };

  proto.getEntities = function (entityStates) {
    var filter = getFilter(entityStates);
    return this._entities.filter(filter);
  };

  proto._checkOperation = function(operationName) {
    this._entities.forEach(function (entity) {
      entity && entity.entityAspect._checkOperation(operationName);
    });
    // for chaining;
    return this;
  };

  // do not expose this method. It is doing a special purpose INCOMPLETE fast detach operation
  // just for the entityManager clear method - the entityGroup will be in an inconsistent state
  // after this op, which is ok because it will be thrown away.
  proto._clear = function () {
    this._entities.forEach(function (entity) {
      if (entity != null) {
        entity.entityAspect._detach();
      }
    });
    this._entities = null;
    this._indexMap = null;
    this._emptyIndexes = null;
  };

  proto._updateFkVal = function (fkProp, oldValue, newValue) {
    var fkPropName = fkProp.name;
    this._entities.forEach(function (entity) {
      if (entity != null) {
        if (entity.getProperty(fkPropName) == oldValue) {
          entity.setProperty(fkPropName, newValue);
        }
      }
    });
  }

  proto._fixupKey = function (tempValue, realValue) {
    // single part keys appear directly in map
    var ix = this._indexMap[tempValue];
    if (ix === undefined) {
      throw new Error("Internal Error in key fixup - unable to locate entity");
    }
    var entity = this._entities[ix];
    var keyPropName = entity.entityType.keyProperties[0].name;
    // fks on related entities will automatically get updated by this as well
    entity.setProperty(keyPropName, realValue);
    delete entity.entityAspect.hasTempKey;
    delete this._indexMap[tempValue];
    this._indexMap[realValue] = ix;
  };

  proto._replaceKey = function (oldKey, newKey) {
    var ix = this._indexMap[oldKey._keyInGroup];
    delete this._indexMap[oldKey._keyInGroup];
    this._indexMap[newKey._keyInGroup] = ix;
  };

  function getFilter(entityStates) {
    if (!entityStates) {
      return function (e) {
        return !!e;
      };
    } else if (entityStates.length === 1) {
      var entityState = entityStates[0];
      return function (e) {
        return !!e && e.entityAspect.entityState === entityState;
      };
    } else {
      return function (e) {
        return !!e && -1 !== entityStates.indexOf(e.entityAspect.entityState);
      };
    }
  }

  return ctor;

})();

// do not expose EntityGroup - internal only


;/**
@module breeze
**/

var EntityManager = (function () {
  /**
  Instances of the EntityManager contain and manage collections of entities, either retrieved from a backend datastore or created on the client.
  @class EntityManager
  **/

  /**
  At its most basic an EntityManager can be constructed with just a service name
  @example
      var entityManager = new EntityManager( "breeze/NorthwindIBModel");
  This is the same as calling it with the following configuration object
  @example
      var entityManager = new EntityManager( {serviceName: "breeze/NorthwindIBModel" });
  Usually however, configuration objects will contain more than just the 'serviceName';
  @example
      var metadataStore = new MetadataStore();
      var entityManager = new EntityManager( {
          serviceName: "breeze/NorthwindIBModel",
          metadataStore: metadataStore
      });
  or
  @example
      return new QueryOptions({
          mergeStrategy: obj,
          fetchStrategy: this.fetchStrategy
      });
      var queryOptions = new QueryOptions({
          mergeStrategy: MergeStrategy.OverwriteChanges,
          fetchStrategy: FetchStrategy.FromServer
      });
      var validationOptions = new ValidationOptions({
          validateOnAttach: true,
          validateOnSave: true,
          validateOnQuery: false
      });
      var entityManager = new EntityManager({
          serviceName: "breeze/NorthwindIBModel",
          queryOptions: queryOptions,
          validationOptions: validationOptions
      });
  @method <ctor> EntityManager
  @param [config] {Object|String} Configuration settings or a service name.
  @param [config.serviceName] {String}
  @param [config.dataService] {DataService} An entire DataService (instead of just the serviceName above).
  @param [config.metadataStore=MetadataStore.defaultInstance] {MetadataStore}
  @param [config.queryOptions] {QueryOptions}
  @param [config.saveOptions] {SaveOptions}
  @param [config.validationOptions=ValidationOptions.defaultInstance] {ValidationOptions}
  @param [config.keyGeneratorCtor] {Function}
  **/
  var ctor = function EntityManager(config) {

    if (arguments.length > 1) {
      throw new Error("The EntityManager ctor has a single optional argument that is either a 'serviceName' or a configuration object.");
    }
    if (arguments.length === 0) {
      config = { serviceName: "" };
    } else if (typeof config === 'string') {
      config = { serviceName: config };
    }

    updateWithConfig(this, config, true);

    this.entityChanged = new Event("entityChanged", this);
    this.validationErrorsChanged = new Event("validationErrorsChanged", this);
    this.hasChangesChanged = new Event("hasChangesChanged", this);

    this.clear();

  };

  var proto = ctor.prototype;
  proto._$typeName = "EntityManager";
  Event.bubbleEvent(proto, null);

  /**
  General purpose property set method.  Any of the properties documented below
  may be set.
  @example
      // assume em1 is a previously created EntityManager
      // where we want to change some of its settings.
      em1.setProperties( {
          serviceName: "breeze/foo"
      });
  @method setProperties
  @param config {Object}
  @param [config.serviceName] {String}
  @param [config.dataService] {DataService}
  @param [config.queryOptions] {QueryOptions}
  @param [config.saveOptions] {SaveOptions}
  @param [config.validationOptions] {ValidationOptions}
  @param [config.keyGeneratorCtor] {Function}
  **/
  proto.setProperties = function (config) {
    updateWithConfig(this, config, false);
  };

  function updateWithConfig(em, config, isCtor) {
    var defaultQueryOptions = isCtor ? QueryOptions.defaultInstance : em.queryOptions;
    var defaultSaveOptions = isCtor ? SaveOptions.defaultInstance : em.saveOptions;
    var defaultValidationOptions = isCtor ? ValidationOptions.defaultInstance : em.validationOptions;


    var configParam = assertConfig(config)
        .whereParam("serviceName").isOptional().isString()
        .whereParam("dataService").isOptional().isInstanceOf(DataService)
        .whereParam("queryOptions").isInstanceOf(QueryOptions).isOptional().withDefault(defaultQueryOptions)
        .whereParam("saveOptions").isInstanceOf(SaveOptions).isOptional().withDefault(defaultSaveOptions)
        .whereParam("validationOptions").isInstanceOf(ValidationOptions).isOptional().withDefault(defaultValidationOptions)
        .whereParam("keyGeneratorCtor").isFunction().isOptional();
    if (isCtor) {
      configParam = configParam
          .whereParam("metadataStore").isInstanceOf(MetadataStore).isOptional().withDefault(new MetadataStore());
    }
    configParam.applyAll(em);

    // insure that entityManager's options versions are completely populated
    __updateWithDefaults(em.queryOptions, defaultQueryOptions);
    __updateWithDefaults(em.saveOptions, defaultSaveOptions);
    __updateWithDefaults(em.validationOptions, defaultValidationOptions);

    if (config.serviceName) {
      em.dataService = new DataService({
        serviceName: em.serviceName
      });
    }
    em.serviceName = em.dataService && em.dataService.serviceName;

    em.keyGeneratorCtor = em.keyGeneratorCtor || KeyGenerator;
    if (isCtor || config.keyGeneratorCtor) {
      em.keyGenerator = new em.keyGeneratorCtor();
    }
  }

  /**
  The service name associated with this EntityManager.

  __readOnly__
  @property serviceName {String}
  **/

  /**
  The DataService name associated with this EntityManager.

  __readOnly__
  @property dataService {DataService}
  **/

  /**
  The {{#crossLink "MetadataStore"}}{{/crossLink}} associated with this EntityManager.

  __readOnly__
  @property metadataStore {MetadataStore}
  **/

  /**
  The {{#crossLink "QueryOptions"}}{{/crossLink}} associated with this EntityManager.

  __readOnly__
  @property queryOptions {QueryOptions}
  **/

  /**
  The {{#crossLink "SaveOptions"}}{{/crossLink}} associated with this EntityManager.

  __readOnly__
  @property saveOptions {SaveOptions}
  **/

  /**
  The {{#crossLink "ValidationOptions"}}{{/crossLink}} associated with this EntityManager.

  __readOnly__
  @property validationOptions {ValidationOptions}
  **/

  /**
  The {{#crossLink "KeyGenerator"}}{{/crossLink}} constructor associated with this EntityManager.

  __readOnly__
  @property keyGeneratorCtor {KeyGenerator constructor}
  **/


  // events
  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever a change to any entity in this EntityManager occurs.
  @example
      var em = new EntityManager( {serviceName: "breeze/NorthwindIBModel" });
      em.entityChanged.subscribe(function(changeArgs) {
          // This code will be executed any time any entity within the entityManager is added, modified, deleted or detached for any reason.
          var action = changeArgs.entityAction;
          var entity = changeArgs.entity;
          // .. do something to this entity when it is changed.
      });
  });

  @event entityChanged
  @param entityAction {EntityAction} The {{#crossLink "EntityAction"}}{{/crossLink}} that occured.
  @param entity {Object} The entity that changed.  If this is null, then all entities in the entityManager were affected.
  @param args {Object} Additional information about this event. This will differ based on the entityAction.
  @readOnly
  **/

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever validationErrors change for any entity in this EntityManager.
  @example
      var em = new EntityManager( {serviceName: "breeze/NorthwindIBModel" });
      em.validationErrorsChanged.subscribe(function(changeArgs) {
              // This code will be executed any time any entity within the entityManager experiences a change to its validationErrors collection.
              function (validationChangeArgs) {
                  var entity == validationChangeArgs.entity;
                  var errorsAdded = validationChangeArgs.added;
                  var errorsCleared = validationChangeArgs.removed;
                  // ... do something interesting with the order.
              });
          });
      });
  @event validationErrorsChanged
  @param entity {Entity} The entity on which the validation errors have been added or removed.
  @param added {Array of ValidationError} An array containing any newly added {{#crossLink "ValidationError"}}{{/crossLink}}s
  @param removed {Array of ValidationError} An array containing any newly removed {{#crossLink "ValidationError"}}{{/crossLink}}s. This is those
  errors that have been 'fixed'
  @readOnly
  **/

  // class methods

  /**
  Creates a new entity of a specified type and optionally initializes it. By default the new entity is created with an EntityState of Added
  but you can also optionally specify an EntityState.  An EntityState of 'Detached' will insure that the entity is created but not yet added
  to the EntityManager.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      // create and add an entity;
      var emp1 = em1.createEntity("Employee");
      // create and add an initialized entity;
      var emp2 = em1.createEntity("Employee", { lastName: Smith", firstName: "John" });
      // create and attach (not add) an initialized entity
      var emp3 = em1.createEntity("Employee", { id: 435, lastName: Smith", firstName: "John" }, EntityState.Unchanged);
      // create but don't attach an entity;
      var emp4 = em1.createEntity("Employee", { id: 435, lastName: Smith", firstName: "John" }, EntityState.Detached);

  @method createEntity
  @param entityType {String|EntityType} The EntityType or the name of the type for which an instance should be created.
  @param [initialValues=null] {Config object} - Configuration object of the properties to set immediately after creation.
  @param [entityState=EntityState.Added] {EntityState} - The EntityState of the entity after being created and added to this EntityManager.
  @param [mergeStrategy=MergeStrategy.Disallowed] {MergeStrategy} - How to handle conflicts if an entity with the same key already exists within this EntityManager.
  @return {Entity} A new Entity of the specified type.
  **/
  proto.createEntity = function (entityType, initialValues, entityState, mergeStrategy) {
    assertParam(entityType, "entityType").isString().or().isInstanceOf(EntityType).check();
    assertParam(entityState, "entityState").isEnumOf(EntityState).isOptional().check();
    assertParam(mergeStrategy, "mergeStrategy").isEnumOf(MergeStrategy).isOptional().check();
    if (typeof entityType === "string") {
      entityType = this.metadataStore._getEntityType(entityType);
    }
    entityState = entityState || EntityState.Added;
    var entity;
    __using(this, "isLoading", true, function () {
      entity = entityType.createEntity(initialValues);
    });
    if (entityState !== EntityState.Detached) {
      entity = this.attachEntity(entity, entityState, mergeStrategy);
    }
    return entity;
  };


  /**
  Creates a new EntityManager and imports a previously exported result into it.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var bundle = em1.exportEntities();
      // can be stored via the web storage api
      window.localStorage.setItem("myEntityManager", bundle);
      // assume the code below occurs in a different session.
      var bundleFromStorage = window.localStorage.getItem("myEntityManager");
      // and imported
      var em2 = EntityManager.importEntities(bundleFromStorage);
      // em2 will now have a complete copy of what was in em1
  @method importEntities
  @static
  @param exportedString {String} The result of a previous 'exportEntities' call.
  @param [config] {Object} A configuration object.
  @param [config.mergeStrategy] {MergeStrategy} A  {{#crossLink "MergeStrategy"}}{{/crossLink}} to use when
  merging into an existing EntityManager.
  @param [config.metadataVersionFn] {Function} A function that takes two arguments (the current metadataVersion and the imported store's 'name')
  and may be used to perform version checking.
  @return {EntityManager} A new EntityManager.  Note that the return value of this method call is different from that
  provided by the same named method on an EntityManager instance. Use that method if you need additional information
  regarding the imported entities.
  **/
  ctor.importEntities = function (exportedString, config) {
    var em = new EntityManager();
    em.importEntities(exportedString, config);
    return em;
  };

  // instance methods

  /**
  Calls EntityAspect.acceptChanges on every changed entity in this EntityManager.
  @method acceptChanges
  **/
  proto.acceptChanges = function () {
    this.getChanges().map(function(entity) {
      return entity.entityAspect._checkOperation("acceptChanges");
    }).forEach(function (aspect) {
      aspect.acceptChanges();
    });
  };

  /**
  Exports selected entities, all entities of selected types, or an entire EntityManager cache.
  @example
  This method takes a snapshot of an EntityManager that can be stored offline or held in memory.
  Use the `EntityManager.importEntities` method to restore or merge the snapshot
  into another EntityManager at some later time.
  @example
      // let em1 be an EntityManager containing a number of existing entities.
      // export every entity in em1.
      var bundle = em1.exportEntities();
      // save to the browser's local storage
      window.localStorage.setItem("myEntityManager", bundle);
      // later retrieve the export
      var bundleFromStorage = window.localStorage.getItem("myEntityManager");
      // import the retrieved export bundle into another manager
      var em2 = em1.createEmptyCopy();
      em2.importEntities(bundleFromStorage);
      // em2 now has a complete, faithful copy of the entities that were in em1
  You can also control exactly which entities are exported.
  @example
      // get em1's unsaved changes (an array) and export them.
      var changes = em1.getChanges();
      var bundle = em1.exportEntities(changes);
      // merge these entities into em2 which may contains some of the same entities.
      // do NOT overwrite the entities in em2 if they themselves have unsaved changes.
      em2.importEntities(bundle, { mergeStrategy: MergeStrategy.PreserveChanges} );
  Metadata are included in an export by default. You may want to exclude the metadata
  especially if you're exporting just a few entities for local storage.
  @example
      var bundle = em1.exportEntities(arrayOfSelectedEntities, {includeMetadata: false});
      window.localStorage.setItem("goodStuff", bundle);
  You may still express this option as a boolean value although this older syntax is deprecated.
  @example
      // Exclude the metadata (deprecated syntax)
      var bundle = em1.exportEntities(arrayOfSelectedEntities, false);
  You can export all entities of one or more specified EntityTypes.
  @example
      // Export all Customer and Employee entities (and also exclude metadata)
      var bundle = em1.exportEntities(['Customer', 'Employee'], {includeMetadata: false});
  All of the above examples return an export bundle as a string which is the default format.
  You can export the bundle as JSON if you prefer by setting the `asString` option to false.
  @example
      // Export all Customer and Employee entities as JSON and exclude the metadata
      var bundle = em1.exportEntities(['Customer', 'Employee'],
                                      {asString: false, includeMetadata: false});
      // store JSON bundle somewhere ... perhaps indexDb ... and later import as we do here.
      em2.importEntities(bundle);
  @method exportEntities
  @param [entities] {Array of Entity | Array of EntityType | Array of String}
    The entities to export or the EntityType(s) of the entities to export;
    all entities are exported if this parameter is omitted or null.
  @param [config] {Object | Boolean} Export configuration options
  @param [config.asString=true] {Boolean} If true (default), return export bundle as a string.
  @param [config.includeMetadata=true] {Boolean} If true (default), include metadata in the export bundle.
  @return {String | Object} The export bundle either serialized (default) or as a JSON object.
  The bundle contains the metadata (unless excluded) and the entity data grouped by type.
  The entity data include property values, change-state, and temporary key mappings (if any).

  The export bundle is an undocumented, Breeze-internal representation of entity data
  suitable for export, storage, and import. The schema and contents of the bundle may change in future versions of Breeze.
  Manipulate it at your own risk with appropriate caution.
  **/
  proto.exportEntities = function (entities, config) {
    assertParam(entities, "entities").isArray().isEntity()
    .or().isNonEmptyArray().isInstanceOf(EntityType)
    .or().isNonEmptyArray().isString()
    .or().isOptional().check();

    assertParam(config, "config").isObject()
    .or().isBoolean()
    .or().isOptional().check();

    if (config == null) {
      config = { includeMetadata: true, asString: true };
    } else if (typeof config === 'boolean') { // deprecated
      config = { includeMetadata: config, asString: true };
    }

    assertConfig(config)
        .whereParam("asString").isBoolean().isOptional().withDefault(true)
        .whereParam("includeMetadata").isBoolean().isOptional().withDefault(true)
        .applyAll(config);

    var exportBundle = exportEntityGroups(this, entities);
    var json = __extend({}, exportBundle, ["tempKeys", "entityGroupMap"]);

    if (config.includeMetadata) {
      json = __extend(json, this, ["dataService", "saveOptions", "queryOptions", "validationOptions"]);
      json.metadataStore = this.metadataStore.exportMetadata();
    } else {
      json.metadataVersion = breeze.metadataVersion;
      json.metadataStoreName = this.metadataStore.name;
    }

    var result = config.asString ? JSON.stringify(json, null, __config.stringifyPad) : json;
    return result;
  };

  /**
  Imports a previously exported result into this EntityManager.
  @example
  This method can be used to make a complete copy of any previously created entityManager, even if created
  in a previous session and stored in localStorage. The static version of this method performs a
  very similar process.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var bundle = em1.exportEntities();
      // bundle can be stored in window.localStorage or just held in memory.
      var em2 = new EntityManager({
          serviceName: em1.serviceName,
          metadataStore: em1.metadataStore
      });
      em2.importEntities(bundle);
      // em2 will now have a complete copy of what was in em1
  It can also be used to merge the contents of a previously created EntityManager with an
  existing EntityManager with control over how the two are merged.
  @example
      var bundle = em1.exportEntities();
      // assume em2 is another entityManager containing some of the same entities possibly with modifications.
      em2.importEntities(bundle, { mergeStrategy: MergeStrategy.PreserveChanges} );
      // em2 will now contain all of the entities from both em1 and em2.  Any em2 entities with previously
      // made modifications will not have been touched, but all other entities from em1 will have been imported.
  @method importEntities
  @param exportedString {String|Json} The result of a previous 'export' call.
  @param [config] {Object} A configuration object.
  @param [config.mergeStrategy] {MergeStrategy} A  {{#crossLink "MergeStrategy"}}{{/crossLink}} to use when
  merging into an existing EntityManager.
  @param [config.metadataVersionFn] {Function} A function that takes two arguments (the current metadataVersion and the imported store's 'name')
  and may be used to perform version checking.
  @return result {Object}

  result.entities {Array of Entities} The entities that were imported.
  result.tempKeyMap {Object} Mapping from original EntityKey in the import bundle to its corresponding EntityKey in this EntityManager.
  **/
  proto.importEntities = function (exportedString, config) {
    config = config || {};
    assertConfig(config)
        .whereParam("mergeStrategy").isEnumOf(MergeStrategy).isOptional().withDefault(this.queryOptions.mergeStrategy)
        .whereParam("metadataVersionFn").isFunction().isOptional()
        .applyAll(config);
    var that = this;

    var json = (typeof exportedString === "string") ? JSON.parse(exportedString) : exportedString;
    if (json.metadataStore) {
      this.metadataStore.importMetadata(json.metadataStore);
      // the || clause is for backwards compat with an earlier serialization format.
      this.dataService = (json.dataService && DataService.fromJSON(json.dataService)) || new DataService({ serviceName: json.serviceName });

      this.saveOptions = new SaveOptions(json.saveOptions);
      this.queryOptions = QueryOptions.fromJSON(json.queryOptions);
      this.validationOptions = new ValidationOptions(json.validationOptions);
    } else {
      config.metadataVersionFn && config.metadataVersionFn({
        metadataVersion: json.metadataVersion,
        metadataStoreName: json.metadataStoreName
      });
    }

    var tempKeyMap = {};
    json.tempKeys.forEach(function (k) {
      var oldKey = EntityKey.fromJSON(k, that.metadataStore);
      // try to use oldKey if not already used in this keyGenerator.
      tempKeyMap[oldKey.toString()] = new EntityKey(oldKey.entityType, that.keyGenerator.generateTempKeyValue(oldKey.entityType, oldKey.values[0]));
    });
    var entitiesToLink = [];
    config.tempKeyMap = tempKeyMap;
    __wrapExecution(function () {
      that._pendingPubs = [];
    }, function (state) {
      that._pendingPubs.forEach(function (fn) {
        fn();
      });
      that._pendingPubs = null;
      that._hasChangesAction && that._hasChangesAction();
    }, function () {
      __objectForEach(json.entityGroupMap, function (entityTypeName, jsonGroup) {
        var entityType = that.metadataStore._getEntityType(entityTypeName, true);
        var targetEntityGroup = findOrCreateEntityGroup(that, entityType);
        var entities = importEntityGroup(targetEntityGroup, jsonGroup, config);
        if (entities && entities.length) {
          entitiesToLink = entitiesToLink.concat(entities);
        }
      });
      entitiesToLink.forEach(function (entity) {
        if (!entity.entityAspect.entityState.isDeleted()) {
          that._linkRelatedEntities(entity);
        }
      });
    });
    return {
      entities: entitiesToLink,
      tempKeyMapping: tempKeyMap
    };
  };


  /**
  Clears this EntityManager's cache but keeps all other settings. Note that this
  method is not as fast as creating a new EntityManager via 'new EntityManager'.
  This is because clear actually detaches all of the entities from the EntityManager.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      em1.clear();
      // em1 is will now contain no entities, but all other setting will be maintained.
  @method clear
  **/
  proto.clear = function () {
    __objectMap(this._entityGroupMap, function (key, entityGroup) {
      return entityGroup._checkOperation();
    }).forEach(function(entityGroup) {
      entityGroup._clear();
    });

    this._entityGroupMap = {};
    this._unattachedChildrenMap = new UnattachedChildrenMap();
    this.keyGenerator = new this.keyGeneratorCtor();
    this.entityChanged.publish({ entityAction: EntityAction.Clear });
    this._setHasChanges(false);
  };


  /**
  Creates an empty copy of this EntityManager
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var em2 = em1.createEmptyCopy();
      // em2 is a new EntityManager with all of em1's settings
      // but no entities.
  @method createEmptyCopy
  @return {EntityManager} A new EntityManager.
  **/
  proto.createEmptyCopy = function () {
    var copy = new ctor(__extend({}, this,
        ["dataService", "metadataStore", "queryOptions", "saveOptions", "validationOptions", "keyGeneratorCtor"]));
    return copy;
  };

  /**
  Attaches an entity to this EntityManager with an  {{#crossLink "EntityState"}}{{/crossLink}} of 'Added'.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var cust1 = custType.createEntity();
      em1.addEntity(cust1);
  Note that this is the same as using 'attachEntity' with an {{#crossLink "EntityState"}}{{/crossLink}} of 'Added'.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var cust1 = custType.createEntity();
      em1.attachEntity(cust1, EntityState.Added);
  @method addEntity
  @param entity {Entity} The entity to add.
  @return {Entity} The added entity.
  **/
  proto.addEntity = function (entity) {
    return this.attachEntity(entity, EntityState.Added);
  };

  /**
  Attaches an entity to this EntityManager with a specified {{#crossLink "EntityState"}}{{/crossLink}}.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var cust1 = custType.createEntity();
      em1.attachEntity(cust1, EntityState.Added);
  @method attachEntity
  @param entity {Entity} The entity to add.
  @param [entityState=EntityState.Unchanged] {EntityState} The EntityState of the newly attached entity. If omitted this defaults to EntityState.Unchanged.
  @param [mergeStrategy=MergeStrategy.Disallowed] {MergeStrategy} How the specified entity should be merged into the EntityManager if this EntityManager already contains an entity with the same key.
  @return {Entity} The attached entity.
  **/
  proto.attachEntity = function (entity, entityState, mergeStrategy) {
    assertParam(entity, "entity").isRequired().check();
    this.metadataStore._checkEntityType(entity);
    entityState = assertParam(entityState, "entityState").isEnumOf(EntityState).isOptional().check(EntityState.Unchanged);
    mergeStrategy = assertParam(mergeStrategy, "mergeStrategy").isEnumOf(MergeStrategy).isOptional().check(MergeStrategy.Disallowed);

    if (entity.entityType.metadataStore !== this.metadataStore) {
      throw new Error("Cannot attach this entity because the EntityType (" + entity.entityType.name +
          ") and MetadataStore associated with this entity does not match this EntityManager's MetadataStore.");
    }
    var aspect = entity.entityAspect;
    if (aspect) {
      // to avoid reattaching an entity in progress
      if (aspect._inProcessEntity) return aspect._inProcessEntity;
    } else {
      // this occur's when attaching an entity created via new instead of via createEntity.
      aspect = new EntityAspect(entity);
    }
    var manager = aspect.entityManager;
    if (manager) {
      if (manager === this) {
        return entity;
      } else {
        throw new Error("This entity already belongs to another EntityManager");
      }
    }

    var that = this;
    var attachedEntity;
    __using(this, "isLoading", true, function () {
      if (entityState.isAdded()) {
        checkEntityKey(that, entity);
      }
      // attachedEntity === entity EXCEPT in the case of a merge.
      attachedEntity = that._attachEntityCore(entity, entityState, mergeStrategy);
      aspect._inProcessEntity = attachedEntity;
      try {
        // entity ( not attachedEntity) is deliberate here.
        attachRelatedEntities(that, entity, entityState, mergeStrategy);
      } finally {
        // insure that _inProcessEntity is cleared.
        aspect._inProcessEntity = null;
      }
    });
    if (this.validationOptions.validateOnAttach) {
      attachedEntity.entityAspect.validateEntity();
    }
    if (!entityState.isUnchanged()) {
      this._notifyStateChange(attachedEntity, true);
    }
    this.entityChanged.publish({ entityAction: EntityAction.Attach, entity: attachedEntity });

    return attachedEntity;
  };


  /**
  Detaches an entity from this EntityManager.
  @example
      // assume em1 is an EntityManager containing a number of existing entities.
      // assume cust1 is a customer Entity previously attached to em1
      em1.detachEntity(cust1);
      // em1 will now no longer contain cust1 and cust1 will have an
      // entityAspect.entityState of EntityState.Detached
  @method detachEntity
  @param entity {Entity} The entity to detach.
  @return {Boolean} Whether the entity could be detached. This will return false if the entity is already detached or was never attached.
  **/
  proto.detachEntity = function (entity) {
    assertParam(entity, "entity").isEntity().check();
    var aspect = entity.entityAspect;
    if (!aspect) {
      // no aspect means in couldn't appear in any group
      return false;
    }

    if (aspect.entityManager !== this) {
      throw new Error("This entity does not belong to this EntityManager.");
    }
    return aspect.setDetached();
  };

  /**
  Fetches the metadata associated with the EntityManager's current 'serviceName'.  This call
  occurs internally before the first query to any service if the metadata hasn't already been
  loaded.
  @example
  Usually you will not actually process the results of a fetchMetadata call directly, but will instead
  ask for the metadata from the EntityManager after the fetchMetadata call returns.
  @example
      var em1 = new EntityManager( "breeze/NorthwindIBModel");
      em1.fetchMetadata()
        .then(function() {
            var metadataStore = em1.metadataStore;
            // do something with the metadata
        }).fail(function(exception) {
            // handle exception here
        });
  @method fetchMetadata
  @async
  @param [callback] {Function} Function called on success.

  successFunction([schema])
  @param [callback.schema] {Object} The raw Schema object from metadata provider - Because this schema will differ depending on the metadata provider
  it is usually better to access metadata via the 'metadataStore' property of the EntityManager after this method's Promise or callback completes.
  @param [errorCallback] {Function} Function called on failure.

  failureFunction([error])
  @param [errorCallback.error] {Error} Any error that occured wrapped into an Error object.
  @return {Promise}
    - Properties on the promise success result
      - schema {Object} The raw Schema object from metadata provider - Because this schema will differ depending on the metadata provider
        it is usually better to access metadata via the 'metadataStore' property of the EntityManager instead of using this 'raw' data.
  **/
  proto.fetchMetadata = function (dataService, callback, errorCallback) {
    if (typeof (dataService) === "function") {
      // legacy support for when dataService was not an arg. i.e. first arg was callback
      errorCallback = callback;
      callback = dataService;
      dataService = null;
    } else {
      assertParam(dataService, "dataService").isInstanceOf(DataService).isOptional().check();
      assertParam(callback, "callback").isFunction().isOptional().check();
      assertParam(errorCallback, "errorCallback").isFunction().isOptional().check();
    }

    var promise = this.metadataStore.fetchMetadata(dataService || this.dataService);
    return promiseWithCallbacks(promise, callback, errorCallback);
  };

  /**
  Executes the specified query.
  @example
  This method can be called using a 'promises' syntax ( recommended)
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders");
      em.executeQuery(query).then( function(data) {
          var orders = data.results;
          ... query results processed here
      }).fail( function(err) {
          ... query failure processed here
      });
  or with callbacks
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders");
      em.executeQuery(query,
          function(data) {
              var orders = data.results;
              ... query results processed here
          },
          function(err) {
              ... query failure processed here
          });
  Either way this method is the same as calling the The {{#crossLink "EntityQuery"}}{{/crossLink}} 'execute' method.
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders").using(em);
      query.execute().then( function(data) {
          var orders = data.results;
          ... query results processed here
      }).fail( function(err) {
          ... query failure processed here
      });

  @method executeQuery
  @async
  @param query {EntityQuery|String}  The {{#crossLink "EntityQuery"}}{{/crossLink}} or OData query string to execute.
  @param [callback] {Function} Function called on success.

  successFunction([data])
  @param callback.data {Object}
  @param callback.data.results {Array of Entity}
  @param callback.data.query {EntityQuery} The original query
  @param callback.data.entityManager {EntityManager} The EntityManager.
  @param callback.data.httpResponse {HttpResponse} The HttpResponse returned from the server.
  @param callback.data.inlineCount {Integer} Only available if 'inlineCount(true)' was applied to the query.  Returns the count of
  items that would have been returned by the query before applying any skip or take operators, but after any filter/where predicates
  would have been applied.
  @param callback.data.retrievedEntities {Array of Entity} All entities returned by the query.  Differs from results when .expand() is used.

  @param [errorCallback] {Function} Function called on failure.

  failureFunction([error])
  @param [errorCallback.error] {Error} Any error that occured wrapped into an Error object.
  @param [errorCallback.error.query] The query that caused the error.
  @param [errorCallback.error.entityManager] The query that caused the error.
  @param [errorCallback.error.httpResponse] {HttpResponse} The HttpResponse returned from the server.


  @return {Promise}
    - Properties on the promise success result
      - results {Array of Entity}
      - query {EntityQuery} The original query
      - entityManager {EntityManager} The EntityManager.
      - httpResponse {HttpResponse} The  HttpResponse returned from the server.
      - [inlineCount] {Integer} Only available if 'inlineCount(true)' was applied to the query.  Returns the count of
    items that would have been returned by the query before applying any skip or take operators, but after any filter/where predicates
    would have been applied.
  **/
  proto.executeQuery = function (query, callback, errorCallback) {
    assertParam(query, "query").isInstanceOf(EntityQuery).or().isString().check();
    assertParam(callback, "callback").isFunction().isOptional().check();
    assertParam(errorCallback, "errorCallback").isFunction().isOptional().check();
    var promise;
    // 'resolve' methods create a new typed object with all of its properties fully resolved against a list of sources.
    // Thought about creating a 'normalized' query with these 'resolved' objects
    // but decided not to because the 'query' may not be an EntityQuery (it can be a string) and hence might not have a queryOptions or dataServices property on it.
    var queryOptions = QueryOptions.resolve([ query.queryOptions, this.queryOptions, QueryOptions.defaultInstance]);
    var dataService = DataService.resolve([ query.dataService, this.dataService]);

    if ((!dataService.hasServerMetadata ) || this.metadataStore.hasMetadataFor(dataService.serviceName)) {
      promise = executeQueryCore(this, query, queryOptions, dataService);
    } else {
      var that = this;
      promise = this.fetchMetadata(dataService).then(function () {
        return executeQueryCore(that, query, queryOptions, dataService);
      });
    }

    return promiseWithCallbacks(promise, callback, errorCallback);
  };

  /**
  Executes the specified query against this EntityManager's local cache.

  @example
  Because this method is executed immediately there is no need for a promise or a callback
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders");
      var orders = em.executeQueryLocally(query);

  Note that this can also be accomplished using the 'executeQuery' method with
  a FetchStrategy of FromLocalCache and making use of the Promise or callback
  @example
      var em = new EntityManager(serviceName);
      var query = new EntityQuery("Orders").using(FetchStrategy.FromLocalCache);
      em.executeQuery(query).then( function(data) {
          var orders = data.results;
          ... query results processed here
      }).fail( function(err) {
          ... query failure processed here
      });
  @method executeQueryLocally
  @param query {EntityQuery}  The {{#crossLink "EntityQuery"}}{{/crossLink}} to execute.
  @return  {Array of Entity}  Array of entities from cache that satisfy the query
  **/
  proto.executeQueryLocally = function (query) {
    return executeQueryLocallyCore(this, query).results;
  }

  function executeQueryLocallyCore(em, query) {
    assertParam(query, "query").isInstanceOf(EntityQuery).check();

    var metadataStore = em.metadataStore;
    var entityType = query._getFromEntityType(metadataStore, true);
    // there may be multiple groups is this is a base entity type.
    var groups = findOrCreateEntityGroups(em, entityType);
    // filter then order then skip then take
    var filterFunc = query.wherePredicate && query.wherePredicate.toFunction({ entityType: entityType});

    var queryOptions = QueryOptions.resolve([ query.queryOptions, em.queryOptions, QueryOptions.defaultInstance]);
    var includeDeleted = queryOptions.includeDeleted === true;

    var newFilterFunc = function (entity) {
      return entity && (includeDeleted || !entity.entityAspect.entityState.isDeleted()) && (filterFunc ? filterFunc(entity) : true);
    };

    var result = [];
    // TODO: mapMany
    groups.forEach(function (group) {
      var entities = group._entities.filter(newFilterFunc);
      if (entities.length) {
          result = result.length ? result.concat(entities) : entities;
      }
    });

    var orderByComparer = query.orderByClause && query.orderByClause.getComparer(entityType);
    if (orderByComparer) {
      result.sort(orderByComparer);
    }

    if (query.inlineCountEnabled) {
      var inlineCount = result.length;
    }

    var skipCount = query.skipCount;
    if (skipCount) {
      result = result.slice(skipCount);
    }
    var takeCount = query.takeCount;
    if (takeCount) {
      result = result.slice(0, takeCount);
    }

    var selectClause = query.selectClause;
    if (selectClause) {
      var selectFn = selectClause.toFunction();
      result = result.map(selectFn);
    }
    return {results: result, inlineCount: inlineCount};
  };

  /**
  Saves either a list of specified entities or all changed entities within this EntityManager. If there are no changes to any of the entities
  specified then there will be no server side call made but a valid 'empty' saveResult will still be returned.
  @example
  Often we will be saving all of the entities within an EntityManager that are either added, modified or deleted
  and we will let the 'saveChanges' call determine which entities these are.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      // This could include added, modified and deleted entities.
      em.saveChanges().then(function(saveResult) {
          var savedEntities = saveResult.entities;
          var keyMappings = saveResult.keyMappings;
      }).fail(function (e) {
          // e is any exception that was thrown.
      });
  But we can also control exactly which entities to save and can specify specific SaveOptions
  @example
      // assume entitiesToSave is an array of entities to save.
      var saveOptions = new SaveOptions({ allowConcurrentSaves: true });
      em.saveChanges(entitiesToSave, saveOptions).then(function(saveResult) {
          var savedEntities = saveResult.entities;
          var keyMappings = saveResult.keyMappings;
      }).fail(function (e) {
          // e is any exception that was thrown.
      });
  Callback methods can also be used
  @example
      em.saveChanges(entitiesToSave, null,
      function(saveResult) {
                  var savedEntities = saveResult.entities;
                  var keyMappings = saveResult.keyMappings;
              }, function (e) {
                  // e is any exception that was thrown.
              }
      );
  @method saveChanges
  @async
  @param [entities] {Array of Entity} The list of entities to save.
  Every entity in that list will be sent to the server, whether changed or unchanged,
  as long as it is attached to this EntityManager.
  If this parameter is omitted, null or empty (the usual case),
  every entity with pending changes in this EntityManager will be saved.
  @param [saveOptions] {SaveOptions} {{#crossLink "SaveOptions"}}{{/crossLink}} for the save - will default to
  {{#crossLink "EntityManager/saveOptions"}}{{/crossLink}} if null.
  @param [callback] {Function} Function called on success.

  successFunction([saveResult])
  @param [callback.saveResult] {Object}
  @param [callback.saveResult.entities] {Array of Entity} The saved entities - with any temporary keys converted into 'real' keys.
  These entities are actually references to entities in the EntityManager cache that have been updated as a result of the
  save.
  @param [callback.saveResult.keyMappings] {Array of keyMappings} Each keyMapping has the following properties: 'entityTypeName', 'tempValue' and 'realValue'
  @param [callback.saveResult.httpResponse] {HttpResponse} The raw HttpResponse returned from the server.

  @param [errorCallback] {Function} Function called on failure.

  failureFunction([error])
  @param [errorCallback.error] {Error} Any error that occured wrapped into an Error object.
  @param [errorCallback.error.entityErrors] { Array of server side errors }  These are typically validation errors but are generally any error that can be easily isolated to a single entity.
  @param [errorCallback.error.httpResponse] {HttpResponse} The raw HttpResponse returned from the server.
  @param [errorCallback.error.saveResult] {Object} Some dataservice adapters return a 'saveResult' object
  when the failing save operation is non-transactional meaning some entities could be saved while others were not.
  The 'saveResult' object identifies both that entities that were saved (with their keyMapping)
  and that entities that were not saved (with their errors).

  @return {Promise} Promise
  **/
  proto.saveChanges = function (entities, saveOptions, callback, errorCallback) {
    assertParam(entities, "entities").isOptional().isArray().isEntity().check();
    assertParam(saveOptions, "saveOptions").isInstanceOf(SaveOptions).isOptional().check();
    assertParam(callback, "callback").isFunction().isOptional().check();
    assertParam(errorCallback, "errorCallback").isFunction().isOptional().check();

    saveOptions = saveOptions || this.saveOptions || SaveOptions.defaultInstance;
    var isFullSave = entities == null;
    var entitiesToSave = getEntitiesToSave(this, entities);

    if (entitiesToSave.length === 0) {
      var result = { entities: [], keyMappings: [] };
      if (callback) callback(result);
      return Q.resolve(result);
    }

    if (!saveOptions.allowConcurrentSaves) {
      var anyPendingSaves = entitiesToSave.some(function (entity) {
        return entity.entityAspect.isBeingSaved;
      });
      if (anyPendingSaves) {
        var err = new Error("Concurrent saves not allowed - SaveOptions.allowConcurrentSaves is false");
        if (errorCallback) errorCallback(err);
        return Q.reject(err);
      }
    }

    clearServerErrors(entitiesToSave);

    var valError = this.saveChangesValidateOnClient(entitiesToSave);
    if (valError) {
      if (errorCallback) errorCallback(valError);
      return Q.reject(valError);
    }

    var dataService = DataService.resolve([saveOptions.dataService, this.dataService]);
    var saveContext = {
      entityManager: this,
      dataService: dataService,
      processSavedEntities: processSavedEntities,
      resourceName: saveOptions.resourceName || this.saveOptions.resourceName || "SaveChanges"
    };

    // TODO: need to check that if we are doing a partial save that all entities whose temp keys
    // are referenced are also in the partial save group

    var saveBundle = { entities: entitiesToSave, saveOptions: saveOptions };


    try { // Guard against exception thrown in dataservice adapter before it goes async
      updateConcurrencyProperties(entitiesToSave);
      return dataService.adapterInstance.saveChanges(saveContext, saveBundle)
          .then(saveSuccess).then(null, saveFail);
    } catch (err) {
      // undo the marking by updateConcurrencyProperties
      markIsBeingSaved(entitiesToSave, false);
      if (errorCallback) errorCallback(err);
      return Q.reject(err);
    }

    function saveSuccess(saveResult) {
      var em = saveContext.entityManager;
      markIsBeingSaved(entitiesToSave, false);
      var savedEntities = saveResult.entities = saveContext.processSavedEntities(saveResult);

      // update _hasChanges after save.
      em._setHasChanges(null);

      // can't do this anymore because other changes might have been made while saved entities in flight.
//      var hasChanges = (isFullSave && haveSameContents(entitiesToSave, savedEntities)) ? false : null;
//      em._setHasChanges(hasChanges);

      if (callback) callback(saveResult);
      return Q.resolve(saveResult);
    }

    function processSavedEntities(saveResult) {

      var savedEntities = saveResult.entities;
      var deletedKeys = saveResult.deletedKeys || [];
      if (savedEntities.length === 0 && deletedKeys.length == 0) {
        return [];
      }
      var keyMappings = saveResult.keyMappings;
      var em = saveContext.entityManager;

      // must occur outside of isLoading block
      fixupKeys(em, keyMappings);

      __using(em, "isLoading", true, function () {

        var mappingContext = new MappingContext({
          query: null, // tells visitAndMerge this is a save instead of a query
          entityManager: em,
          mergeOptions: { mergeStrategy: MergeStrategy.OverwriteChanges },
          dataService: dataService
        });

        // The visitAndMerge operation has been optimized so that we do not actually perform a merge if the
        // the save operation did not actually return the entity - i.e. during OData and Mongo updates and deletes.
        savedEntities = mappingContext.visitAndMerge(savedEntities, { nodeType: "root" });
      });

      // detach any entities found in the em that appear in the deletedKeys list. 
      
      deletedKeys.forEach(function(key) {
        var entityType = em.metadataStore._getEntityType(key.entityTypeName);
        var ekey = new EntityKey(entityType, key.keyValues);
        var entity = em.findEntityByKey(ekey);
        if (entity) {
          entity.entityAspect.setDetached();
        }
      })

      return savedEntities;
    }

    function saveFail(error) {
      markIsBeingSaved(entitiesToSave, false);
      processServerErrors(saveContext, error);
      if (errorCallback) errorCallback(error);
      return Q.reject(error);
    }
  };

  /**
  Run the "saveChanges" pre-save client validation logic.

  This is NOT a general purpose validation method.
  It is intended for utilities that must know if saveChanges
  would reject the save due to client validation errors.

  It only validates entities if the EntityManager's
  {{#crossLink "ValidationOptions"}}{{/crossLink}}.validateOnSave is true.

  @method saveChangesValidateOnClient
  @param entitiesToSave {Array of Entity} The list of entities to save (to validate).
  @return {Error} Validation error or null if no error
  **/
  proto.saveChangesValidateOnClient = function(entitiesToSave) {

    if (this.validationOptions.validateOnSave) {
      var failedEntities = entitiesToSave.filter(function (entity) {
        var aspect = entity.entityAspect;
        var isValid = aspect.entityState.isDeleted() || aspect.validateEntity();
        return !isValid;
      });
      if (failedEntities.length > 0) {
        var valError = new Error("Client side validation errors encountered - see the entityErrors collection on this object for more detail");
        valError.entityErrors = createEntityErrors(failedEntities);
        return valError;
      }
    }
    return null;
  }

  function clearServerErrors(entities) {
    entities.forEach(function (entity) {
      var serverKeys = [];
      var aspect = entity.entityAspect;
      __objectForEach(aspect._validationErrors, function (key, ve) {
        if (ve.isServerError) serverKeys.push(key);
      });
      if (serverKeys.length === 0) return;
      aspect._processValidationOpAndPublish(function () {
        serverKeys.forEach(function (key) {
          aspect._removeValidationError(key);
        });
      });
    });
  }

  function createEntityErrors(entities) {
    var entityErrors = [];
    entities.forEach(function (entity) {
      __objectForEach(entity.entityAspect._validationErrors, function (key, ve) {
        var cfg = __extend({
          entity: entity,
          errorName: ve.validator.name
        }, ve, ["errorMessage", "propertyName", "isServerError"]);
        entityErrors.push(cfg);
      });
    });
    return entityErrors;
  }


  function processServerErrors(saveContext, error) {
    var serverErrors = error.entityErrors;
    if (!serverErrors) return;
    var entityManager = saveContext.entityManager;
    var metadataStore = entityManager.metadataStore;
    error.entityErrors = serverErrors.map(function (serr) {
      var entity = null;
      if (serr.keyValues) {
        var entityType = metadataStore._getEntityType(serr.entityTypeName);
        var ekey = new EntityKey(entityType, serr.keyValues);
        entity = entityManager.findEntityByKey(ekey);
      }

      if (entity) {
        var context = serr.propertyName ?
                      {
                        propertyName: serr.propertyName,
                        property: entityType.getProperty(serr.propertyName)
                      } : {
                      };
        var key = ValidationError.getKey(serr.errorName || serr.errorMessage, serr.propertyName);

        var ve = new ValidationError(null, context, serr.errorMessage, key);
        ve.isServerError = true;
        entity.entityAspect.addValidationError(ve);
      }

      var entityError = __extend({
        entity: entity,
        isServerError: true
      }, serr, ["errorName", "errorMessage", "propertyName"]);
      return entityError;
    });
  }

  // No longer used
//  function haveSameContents(arr1, arr2) {
//    if (arr1.length !== arr2.length) {
//      return false;
//    }
//    for (var i = 0, c = arr1.length; i < c; i++) {
//      if (arr1[i] !== arr2[i]) return false;
//    }
//    return true;
//  }


  proto._findEntityGroup = function (entityType) {
    return this._entityGroupMap[entityType.name];
  };


  /**
  Attempts to locate an entity within this EntityManager by its key.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var employee = em1.getEntityByKey("Employee", 1);
      // employee will either be an entity or null.
  @method getEntityByKey
  @param typeName {EntityType | String} The EntityType or EntityType name for this key.
  @param keyValues {Object|Array of Object} The values for this key - will usually just be a single value; an array is only needed for multipart keys.
  @return {Entity} An Entity or null;
  **/

  /**
  Attempts to locate an entity within this EntityManager by its  {{#crossLink "EntityKey"}}{{/crossLink}}.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var employeeType = em1.metadataStore.getEntityType("Employee");
      var employeeKey = new EntityKey(employeeType, 1);
      var employee = em1.getEntityByKey(employeeKey);
      // employee will either be an entity or null.
  @method getEntityByKey - overload
  @param entityKey {EntityKey} The  {{#crossLink "EntityKey"}}{{/crossLink}} of the Entity to be located.
  @return {Entity} An Entity or null;
  **/
  proto.getEntityByKey = function () {
    var entityKey = createEntityKey(this, arguments).entityKey;
    var entityTypes = entityKey._subtypes || [entityKey.entityType];
    var ek = null;
    // hack use of some to simulate mapFirst logic.
    entityTypes.some(function (et) {
      var group = this._findEntityGroup(et);
      // group version of findEntityByKey doesn't care about entityType
      ek = group && group.findEntityByKey(entityKey);
      return ek;
    }, this);
    return ek;
  };

  /**
  Attempts to fetch an entity from the server by its key with
  an option to check the local cache first. Note the this EntityManager's queryOptions.mergeStrategy
  will be used to merge any server side entity returned by this method.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      em1.fetchEntityByKey("Employee", 1).then(function(result) {
          var employee = result.entity;
          var entityKey = result.entityKey;
          var fromCache = result.fromCache;
      });
  @method fetchEntityByKey
  @async
  @param typeName {EntityType | String} The EntityType or EntityType name for this key.
  @param keyValues {Object|Array of Object} The values for this key - will usually just be a single value; an array is only needed for multipart keys.
  @param checkLocalCacheFirst {Boolean=false} Whether to check this EntityManager first before going to the server. By default, the query will NOT do this.
  @return {Promise}
    - Properties on the promise success result
      - entity {Object} The entity returned or null
      - entityKey {EntityKey} The entityKey of the entity to fetch.
      - fromCache {Boolean} Whether this entity was fetched from the server or was found in the local cache.

  **/

  /**
  Attempts to fetch an entity from the server by its {{#crossLink "EntityKey"}}{{/crossLink}} with
  an option to check the local cache first.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var employeeType = em1.metadataStore.getEntityType("Employee");
      var employeeKey = new EntityKey(employeeType, 1);
      em1.fetchEntityByKey(employeeKey).then(function(result) {
          var employee = result.entity;
          var entityKey = result.entityKey;
          var fromCache = result.fromCache;
      });
  @method fetchEntityByKey - overload
  @async
  @param entityKey {EntityKey} The  {{#crossLink "EntityKey"}}{{/crossLink}} of the Entity to be located.
  @param checkLocalCacheFirst {Boolean=false} Whether to check this EntityManager first before going to the server. By default, the query will NOT do this.
  @return {Promise}
    - Properties on the promise success result
      - entity {Object} The entity returned or null
      - entityKey {EntityKey} The entityKey of the entity to fetch.
      - fromCache {Boolean} Whether this entity was fetched from the server or was found in the local cache.
  **/
  proto.fetchEntityByKey = function () {
    var dataService = DataService.resolve([this.dataService]);
    if ((!dataService.hasServerMetadata) || this.metadataStore.hasMetadataFor(dataService.serviceName)) {
      return fetchEntityByKeyCore(this, arguments);
    } else {
      var that = this;
      var args = arguments;
      return this.fetchMetadata(dataService).then(function () {
        return fetchEntityByKeyCore(that, args);
      });
    }
  };

  function fetchEntityByKeyCore(em, args) {
    var tpl = createEntityKey(em, args);
    var entityKey = tpl.entityKey;
    var checkLocalCacheFirst = tpl.remainingArgs.length === 0 ? false : !!tpl.remainingArgs[0];
    var entity;
    var foundIt = false;
    if (checkLocalCacheFirst) {
      entity = em.getEntityByKey(entityKey);
      foundIt = !!entity;
      if (foundIt &&
        // null the entity if it is deleted and we should exclude deleted entities
          !em.queryOptions.includeDeleted && entity.entityAspect.entityState.isDeleted()) {
        entity = null;
        // but resume looking if we'd overwrite deleted entity with a remote entity
        // note: em.queryOptions is always fully resolved by now
        foundIt = em.queryOptions.mergeStrategy !== MergeStrategy.OverwriteChanges;
      }
    }
    if (foundIt) {
      return Q.resolve({ entity: entity, entityKey: entityKey, fromCache: true });
    } else {
      return EntityQuery.fromEntityKey(entityKey).using(em).execute().then(function (data) {
        entity = (data.results.length === 0) ? null : data.results[0];
        return Q.resolve({ entity: entity, entityKey: entityKey, fromCache: false });
      });
    }
  };

  /**
  [Deprecated] - Attempts to locate an entity within this EntityManager by its  {{#crossLink "EntityKey"}}{{/crossLink}}.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var employeeType = em1.metadataStore.getEntityType("Employee");
      var employeeKey = new EntityKey(employeeType, 1);
      var employee = em1.findEntityByKey(employeeKey);
      // employee will either be an entity or null.
  @method findEntityByKey
  @deprecated - use getEntityByKey instead
  @param entityKey {EntityKey} The  {{#crossLink "EntityKey"}}{{/crossLink}} of the Entity to be located.
  @return {Entity} An Entity or null;
  **/
  proto.findEntityByKey = function (entityKey) {
    return this.getEntityByKey(entityKey);
  };

  /**
  Generates a temporary key for the specified entity.  This is used to insure that newly
  created entities have unique keys and to register that these keys are temporary and
  need to be automatically replaced with 'real' key values once these entities are saved.

  The EntityManager.keyGeneratorCtor property is used internally by this method to actually generate
  the keys - See the  {{#crossLink "_keyGenerator_interface"}}{{/crossLink}} interface description to see
  how a custom key generator can be plugged in.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var customer = custType.createEntity();
      var customerId = em.generateTempKeyValue(customer);
      // The 'customer' entity 'CustomerID' property is now set to a newly generated unique id value
      // This property will change again after a successful save of the 'customer' entity.

      em1.saveChanges().then( function( data) {
          var sameCust1 = data.results[0];
          // cust1 === sameCust1;
          // but cust1.getProperty("CustomerId") != customerId
          // because the server will have generated a new id
          // and the client will have been updated with this
          // new id.
      })

  @method generateTempKeyValue
  @param entity {Entity} The Entity to generate a key for.
  @return {Object} The new key value
  **/
  proto.generateTempKeyValue = function (entity) {
    // TODO - check if this entity is attached to this EntityManager.
    assertParam(entity, "entity").isEntity().check();
    var entityType = entity.entityType;
    var nextKeyValue = this.keyGenerator.generateTempKeyValue(entityType);
    var keyProp = entityType.keyProperties[0];
    entity.setProperty(keyProp.name, nextKeyValue);
    entity.entityAspect.hasTempKey = true;
    return nextKeyValue;
  };

  /**
  Returns whether there are any changed entities of the specified {{#crossLink "EntityType"}}{{/crossLink}}s. A 'changed' Entity has
  has an {{#crossLink "EntityState"}}{{/crossLink}} of either Added, Modified or Deleted.
  @example
  This method can be used to determine if an EntityManager has any changes
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      if ( em1.hasChanges() {
          // do something interesting
      }
  or if it has any changes on to a specific {{#crossLink "EntityType"}}{{/crossLink}}
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      if ( em1.hasChanges(custType) {
          // do something interesting
      }
  or to a collection of {{#crossLink "EntityType"}}{{/crossLink}}s
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var orderType = em1.metadataStore.getEntityType("Order");
      if ( em1.hasChanges( [custType, orderType]) {
          // do something interesting
      }
  @method hasChanges
  @param [entityTypes] {String|Array of String|EntityType|Array of EntityType} The {{#crossLink "EntityType"}}{{/crossLink}}s for which 'changed' entities will be found.
  If this parameter is omitted, all EntityTypes are searched. String parameters are treated as EntityType names.
  @return {Boolean} Whether there were any changed entities.
  **/
  proto.hasChanges = function (entityTypes) {
    if (!this._hasChanges) return false;
    if (entityTypes === undefined) return this._hasChanges;
    return this._hasChangesCore(entityTypes);
  };

  /**
  An {{#crossLink "Event"}}{{/crossLink}} that fires whenever an EntityManager transitions to or from having changes.
  @example
      var em = new EntityManager( {serviceName: "breeze/NorthwindIBModel" });
      em.hasChangesChanged.subscribe(function(args) {
              var hasChangesChanged = args.hasChanges;
              var entityManager = args.entityManager;
          });
      });

  @event hasChangesChanged
  @param entityManager {EntityManager} The EntityManager whose 'hasChanges' status has changed.
  @param hasChanges {Boolean} Whether or not this EntityManager has changes.
  @readOnly
  **/


  // backdoor to "really" check for changes.
  proto._hasChangesCore = function (entityTypes) {
    entityTypes = checkEntityTypes(this, entityTypes);
    var entityGroups = getEntityGroups(this, entityTypes);
    return entityGroups.some(function (eg) {
      return eg && eg.hasChanges();
    });
  };

  /**
  Returns a array of all changed entities of the specified {{#crossLink "EntityType"}}{{/crossLink}}s. A 'changed' Entity has
  has an {{#crossLink "EntityState"}}{{/crossLink}} of either Added, Modified or Deleted.
  @example
  This method can be used to get all of the changed entities within an EntityManager
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var changedEntities = em1.getChanges();
  or you can specify that you only want the changes on a specific {{#crossLink "EntityType"}}{{/crossLink}}
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var changedCustomers = em1.getChanges(custType);
  or to a collection of {{#crossLink "EntityType"}}{{/crossLink}}s
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var orderType = em1.metadataStore.getEntityType("Order");
      var changedCustomersAndOrders = em1.getChanges([custType, orderType]);
  @method getChanges
  @param [entityTypes] {String|Array of String|EntityType|Array of EntityType} The {{#crossLink "EntityType"}}{{/crossLink}}s for which 'changed' entities will be found.
  If this parameter is omitted, all EntityTypes are searched. String parameters are treated as EntityType names.
  @return {Array of Entity} Array of Entities
  **/
  proto.getChanges = function (entityTypes) {
    entityTypes = checkEntityTypes(this, entityTypes);
    return getChangesCore(this, entityTypes);
  };

  /**
  Rejects (reverses the effects) all of the additions, modifications and deletes from this EntityManager.
  Calls EntityAspect.rejectChanges on every changed entity in this EntityManager.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var entities = em1.rejectChanges();

  @method rejectChanges
  @return {Array of Entity} The entities whose changes were rejected. These entities will all have EntityStates of
  either 'Unchanged' or 'Detached'
  **/
  proto.rejectChanges = function () {
    if (!this._hasChanges) return [];
    var changes = getChangesCore(this, null);
    // next line stops individual reject changes from each calling _hasChangesCore
    var aspects = changes.map(function(e) {
      return e.entityAspect._checkOperation("rejectChanges");
    });
    this._hasChanges = false;
    aspects.forEach(function (aspect) {
      aspect.rejectChanges();
    });
    this.hasChangesChanged.publish({ entityManager: this, hasChanges: false });
    return changes;
  };

  /**
  Returns a array of all entities of the specified {{#crossLink "EntityType"}}{{/crossLink}}s with the specified {{#crossLink "EntityState"}}{{/crossLink}}s.
  @example
  This method can be used to get all of the entities within an EntityManager
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var entities = em1.getEntities();
  or you can specify that you only want the changes on a specific {{#crossLink "EntityType"}}{{/crossLink}}
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var customers = em1.getEntities(custType);
  or to a collection of {{#crossLink "EntityType"}}{{/crossLink}}s
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var orderType = em1.metadataStore.getEntityType("Order");
      var customersAndOrders = em1.getChanges([custType, orderType]);
  You can also ask for entities with a particular {{#crossLink "EntityState"}}{{/crossLink}} or EntityStates.
  @example
      // assume em1 is an EntityManager containing a number of preexisting entities.
      var custType = em1.metadataStore.getEntityType("Customer");
      var orderType = em1.metadataStore.getEntityType("Order");
      var addedCustomersAndOrders = em1.getEntities([custType, orderType], EntityState.Added);
  @method getEntities
  @param [entityTypes] {String|Array of String|EntityType|Array of EntityType} The {{#crossLink "EntityType"}}{{/crossLink}}s for which entities will be found.
  If this parameter is omitted, all EntityTypes are searched. String parameters are treated as EntityType names.
  @param [entityState] {EntityState|Array of EntityState} The {{#crossLink "EntityState"}}{{/crossLink}}s for which entities will be found.
  If this parameter is omitted, entities of all EntityStates are returned.
  @return {Array of Entity} Array of Entities
  **/
  proto.getEntities = function (entityTypes, entityStates) {
    entityTypes = checkEntityTypes(this, entityTypes);
    assertParam(entityStates, "entityStates").isOptional().isEnumOf(EntityState).or().isNonEmptyArray().isEnumOf(EntityState).check();

    entityStates = entityStates && validateEntityStates(this, entityStates);
    return getEntitiesCore(this, entityTypes, entityStates);
  };


  // protected methods

  proto._notifyStateChange = function (entity, needsSave) {
    var ecArgs = { entityAction: EntityAction.EntityStateChange, entity: entity };

    if (needsSave) {
      if (!this._hasChanges) this._setHasChanges(true);
    } else {
      // called when rejecting a change or merging an unchanged record.
      // NOTE: this can be slow with lots of entities in the cache.
      // so defer it during a query/import or save and call it once when complete ( if needed).
      if (this._hasChanges) {
        if (this.isLoading) {
          this._hasChangesAction = this._hasChangesAction || function () {
            this._setHasChanges(null);
            this.entityChanged.publish(ecArgs);
          }.bind(this);
          return;
        } else {
          this._setHasChanges(null);
        }
      }
    }
    this.entityChanged.publish(ecArgs);
  };

  proto._setHasChanges = function (hasChanges) {
    if (hasChanges == null) hasChanges = this._hasChangesCore();
    var hadChanges = this._hasChanges;
    this._hasChanges = hasChanges;
    if (hasChanges != hadChanges) {
      this.hasChangesChanged.publish({ entityManager: this, hasChanges: hasChanges });
    }
    this._hasChangesAction = null;
  }

  proto._linkRelatedEntities = function (entity) {
    var em = this;
    var entityAspect = entity.entityAspect;
    // we do not want entityState to change as a result of linkage.
    __using(em, "isLoading", true, function () {

      var unattachedMap = em._unattachedChildrenMap;
      var entityKey = entityAspect.getKey();

    var entityType = entityKey.entityType;
    while (entityType) {
      var keystring = entityKey.toString(entityType);

      // attach any unattachedChildren
      var tuples = unattachedMap.getTuplesByString(keystring);

      if (tuples && tuples.length) {
        tuples.slice(0).forEach(function (tpl) {

          var unattachedChildren = tpl.children.filter(function (e) {
            return e.entityAspect.entityState !== EntityState.Detached;
          });

          var childToParentNp, parentToChildNp;

          // np is usually childToParentNp
          // except with unidirectional 1-n where it is parentToChildNp;
          var np = tpl.navigationProperty;

          if (np.getInverse()) {
            // bidirectional
            childToParentNp = np;
            parentToChildNp = np.getInverse();

            if (parentToChildNp.isScalar) {
              var onlyChild = unattachedChildren[0];
              entity.setProperty(parentToChildNp.name, onlyChild);
              onlyChild.setProperty(childToParentNp.name, entity);
            } else {
              var currentChildren = entity.getProperty(parentToChildNp.name);
              unattachedChildren.forEach(function (child) {
                currentChildren.push(child);
                child.setProperty(childToParentNp.name, entity);
              });
            }
          } else {
            // unidirectional
            // if (np.isScalar || np.parentType !== entity.entityType) {
            if (np.isScalar) {
              // n -> 1  eg: child: OrderDetail parent: Product
              // 1 -> 1 eg child: Employee parent: Employee ( only Manager, no DirectReports property)
              childToParentNp = np;
              unattachedChildren.forEach(function (child) {
                child.setProperty(childToParentNp.name, entity);
              });
            } else {
              // 1 -> n  eg: parent: Region child: Terr
              // TODO: need to remove unattached children from the map after this; only a perf issue.
              parentToChildNp = np;
              var currentChildren = entity.getProperty(parentToChildNp.name);
              unattachedChildren.forEach(function (child) {
                // we know if can't already be there.
                currentChildren._push(child);
              });
            }
          }
          unattachedMap.removeChildrenByString(keystring, childToParentNp);
        });
      }
      entityType = entityType.baseEntityType;
    }


      // now add to unattachedMap if needed.
      entity.entityType.navigationProperties.forEach(function (np) {
        if (np.isScalar) {
          var value = entity.getProperty(np.name);
          // property is already linked up
          if (value) return;
        }

        // first determine if np contains a parent or child
        // having a parentKey means that this is a child
        // if a parent then no need for more work because children will attach to it.
        var parentKey = entityAspect.getParentKey(np);
        if (parentKey) {
          // check for empty keys - meaning that parent id's are not yet set.
          if (parentKey._isEmpty()) return;
          // if a child - look for parent in the em cache
          var parent = em.findEntityByKey(parentKey);
          if (parent) {
            // if found hook it up
            entity.setProperty(np.name, parent);
          } else {
            // else add parent to unresolvedParentMap;
            unattachedMap.addChild(parentKey, np, entity);
          }
        }
      });

      // handle unidirectional 1-x where we set x.fk
      entity.entityType.foreignKeyProperties.forEach(function (fkProp) {
        var invNp = fkProp.inverseNavigationProperty;
        if (!invNp) return;
        // unidirectional fk props only
        var fkValue = entity.getProperty(fkProp.name);
        var parentKey = new EntityKey(invNp.parentType, [fkValue]);
        var parent = em.findEntityByKey(parentKey);

        if (parent) {
          if (invNp.isScalar) {
            parent.setProperty(invNp.name, entity);
          } else {
            if (em.isLoading) {
              parent.getProperty(invNp.name)._push(entity);
            } else {
              parent.getProperty(invNp.name).push(entity);
            }
          }
        } else {
          // else add parent to unresolvedParentMap;
          unattachedMap.addChild(parentKey, invNp, entity);
        }
      });
    });
  };

  // private fns

  // takes in entityTypes as either strings or entityTypes or arrays of either
  // and returns either an entityType or an array of entityTypes or throws an error
  function checkEntityTypes(em, entityTypes) {
    assertParam(entityTypes, "entityTypes").isString().isOptional().or().isNonEmptyArray().isString()
        .or().isInstanceOf(EntityType).or().isNonEmptyArray().isInstanceOf(EntityType).check();
    if (typeof entityTypes === "string") {
      entityTypes = em.metadataStore._getEntityType(entityTypes, false);
    } else if (Array.isArray(entityTypes) && typeof entityTypes[0] === "string") {
      entityTypes = entityTypes.map(function (etName) {
        return em.metadataStore._getEntityType(etName, false);
      });
    }
    return entityTypes;
  }

  function getChangesCore(em, entityTypes) {
    var entityGroups = getEntityGroups(em, entityTypes);

    // TODO: think about writing a core.mapMany method if we see more of these.
    var selected;
    entityGroups.forEach(function (eg) {
      // eg may be undefined or null
      if (!eg) return;
      var entities = eg.getChanges();
      if (selected && selected.length) {
        selected = selected.concat(entities);
      } else {
        selected = entities;
      }
    });
    return selected || [];
  }

  function getEntitiesCore(em, entityTypes, entityStates) {
    var entityGroups = getEntityGroups(em, entityTypes);

    // TODO: think about writing a core.mapMany method if we see more of these.
    var selected;
    entityGroups.forEach(function (eg) {
      // eg may be undefined or null
      if (!eg) return;
      var entities = eg.getEntities(entityStates);
      if (selected && selected.length) {
        selected = selected.concat(entities);
      } else {
        selected = entities;
      }
    });
    return selected || [];
  }

  function createEntityKey(em, args) {
    try {
      if (args[0] instanceof EntityKey) {
        return { entityKey: args[0], remainingArgs: __arraySlice(args, 1) };
      } else if (args.length >= 2) {
        var entityType = (typeof args[0] === 'string') ? em.metadataStore._getEntityType(args[0], false) : args[0];
        return { entityKey: new EntityKey(entityType, args[1]), remainingArgs: __arraySlice(args, 2) };
      }
    } catch (e) {/* throw below */
    }
    throw new Error("Must supply an EntityKey OR an EntityType name or EntityType followed by a key value or an array of key values.");
  }

  function markIsBeingSaved(entities, flag) {
    entities.forEach(function (entity) {
      entity.entityAspect.isBeingSaved = flag;
    });
  }

  function exportEntityGroups(em, entities) {
    var entityGroupMap;
    var first = entities && entities[0];
    if (first) {
      // group entities by entityType and
      // create 'groups' that look like entityGroups.
      entityGroupMap = {};
      if (first.entityType) {
        // assume "entities" is an array of entities;
        entities.forEach(function (e) {
          if (e.entityAspect.entityState == EntityState.Detached) {
            throw new Error("Unable to export an entity with an EntityState of 'Detached'");
          }
          var group = entityGroupMap[e.entityType.name];
          if (!group) {
            group = {};
            group.entityType = e.entityType;
            group._entities = [];
            entityGroupMap[e.entityType.name] = group;
          }
          group._entities.push(e);
        });
      } else {
        // assume "entities" is an array of EntityTypes (or names)
        var entityTypes = checkEntityTypes(em, entities)
        entityTypes.forEach(function(et){
          var group = em._entityGroupMap[et.name];
          if (group && group._entities.length) {
            entityGroupMap[et.name] = group;
          }
        })
      }
    } else if (entities && entities.length === 0) {
      // empty array = export nothing
      entityGroupMap = {};
    } else {
      entityGroupMap = em._entityGroupMap;
    }

    var tempKeys = [];
    var newGroupMap = {};
    __objectForEach(entityGroupMap, function (entityTypeName, entityGroup) {
      newGroupMap[entityTypeName] = exportEntityGroup(entityGroup, tempKeys);
    });

    return { entityGroupMap: newGroupMap, tempKeys: tempKeys };
  }

  function exportEntityGroup(entityGroup, tempKeys) {
    var resultGroup = {};
    var entityType = entityGroup.entityType;
    var dps = entityType.dataProperties;
    var serializerFn = getSerializerFn(entityType);
    var rawEntities = [];
    entityGroup._entities.forEach(function (entity) {
      if (entity) {
        var rawEntity = structuralObjectToJson(entity, dps, serializerFn, tempKeys);
        rawEntities.push(rawEntity);
      }
    });
    resultGroup.entities = rawEntities;
    return resultGroup;
  }

  function structuralObjectToJson(so, dps, serializerFn, tempKeys) {

    var result = {};
    dps.forEach(function (dp) {
      var dpName = dp.name;
      var value = so.getProperty(dpName);
      if (value == null && dp.defaultValue == null) return;

      if (value && dp.isComplexProperty) {
        var coDps = dp.dataType.dataProperties;
        value = __map(value, function (v) {
          return structuralObjectToJson(v, coDps, serializerFn);
        });
      } else {
        value = serializerFn ? serializerFn(dp, value) : value;
        if (dp.isUnmapped) {
          value = __toJSONSafe(value);
        }
      }
      if (value === undefined) return;
      result[dpName] = value;
    });
    var aspect, newAspect;
    if (so.entityAspect) {
      aspect = so.entityAspect;
      var entityState = aspect.entityState;
      newAspect = {
        tempNavPropNames: exportTempKeyInfo(aspect, tempKeys),
        entityState: entityState.name
      };
      if (aspect.extraMetadata) {
        newAspect.extraMetadata = aspect.extraMetadata;
      }
      if (entityState.isModified() || entityState.isDeleted()) {
        newAspect.originalValuesMap = aspect.originalValues;
      }
      result.entityAspect = newAspect;
    } else {
      aspect = so.complexAspect;
      newAspect = {};
      if (aspect.originalValues && !__isEmpty(aspect.originalValues)) {
        newAspect.originalValuesMap = aspect.originalValues;
      }

      result.complexAspect = newAspect;
    }

    return result;
  }

  function exportTempKeyInfo(entityAspect, tempKeys) {
    var entity = entityAspect.entity;
    if (entityAspect.hasTempKey) {
      tempKeys.push(entityAspect.getKey().toJSON());
    }
    // create map for this entity with foreignKeys that are 'temporary'
    // map -> key: tempKey, value: fkPropName
    var tempNavPropNames;
    entity.entityType.navigationProperties.forEach(function (np) {
      if (np.relatedDataProperties) {
        var relatedValue = entity.getProperty(np.name);
        if (relatedValue && relatedValue.entityAspect.hasTempKey) {
          tempNavPropNames = tempNavPropNames || [];
          tempNavPropNames.push(np.name);
        }
      }
    });
    return tempNavPropNames;
  }

  function importEntityGroup(entityGroup, jsonGroup, config) {

    var tempKeyMap = config.tempKeyMap;

    var entityType = entityGroup.entityType;
    var mergeStrategy = config.mergeStrategy;

    var targetEntity = null;

    var em = entityGroup.entityManager;
    var entityChanged = em.entityChanged;
    var entitiesToLink = [];
    var rawValueFn = DataProperty.getRawValueFromClient;
    jsonGroup.entities.forEach(function (rawEntity) {
      var newAspect = rawEntity.entityAspect;

      var entityKey = entityType.getEntityKeyFromRawEntity(rawEntity, rawValueFn);
      var entityState = EntityState.fromName(newAspect.entityState);
      if (!entityState || entityState == EntityState.Detached ) {
        throw new Error("Only entities with a non detached entity state may be imported.");
      }

      // Merge if raw entity is in cache
      // UNLESS this is a new entity w/ a temp key
      // Cannot safely merge such entities even
      // if could match temp key to an entity in cache.
      var newTempKey = entityState.isAdded() && getMappedKey(tempKeyMap, entityKey);
      targetEntity = newTempKey ? null : entityGroup.findEntityByKey(entityKey);

      if (targetEntity) {
        if (mergeStrategy === MergeStrategy.SkipMerge) {
          // deliberate fall thru
        } else if (mergeStrategy === MergeStrategy.Disallowed) {
          throw new Error("A MergeStrategy of 'Disallowed' prevents " + entityKey.toString() + " from being merged");
        } else {
          var targetEntityState = targetEntity.entityAspect.entityState;
          var wasUnchanged = targetEntityState.isUnchanged();
          if (mergeStrategy === MergeStrategy.OverwriteChanges || wasUnchanged) {
            entityType._updateTargetFromRaw(targetEntity, rawEntity, rawValueFn);
            targetEntity.entityAspect.setEntityState(entityState);
            entityChanged.publish({ entityAction: EntityAction.MergeOnImport, entity: targetEntity });
          }
        }
      } else {
        targetEntity = entityType._createInstanceCore();
        entityType._updateTargetFromRaw(targetEntity, rawEntity, rawValueFn);
        if (newTempKey) {
          targetEntity.entityAspect.hasTempKey = true;
          // fixup pk
          targetEntity.setProperty(entityType.keyProperties[0].name, newTempKey.values[0]);

          // fixup foreign keys
          // This is safe because the entity is detached here and therefore originalValues will not be updated.
          if (newAspect.tempNavPropNames) {
            newAspect.tempNavPropNames.forEach(function (npName) {
              var np = entityType.getNavigationProperty(npName);
              var fkPropName = np.relatedDataProperties[0].name;
              var oldFkValue = targetEntity.getProperty(fkPropName);
              var fk = new EntityKey(np.entityType, [oldFkValue]);
              var newFk = getMappedKey(tempKeyMap, fk);
              targetEntity.setProperty(fkPropName, newFk.values[0]);
            });
          }
        }
        // Now performed in attachEntity
        targetEntity = entityGroup.attachEntity(targetEntity, entityState);
        entityChanged.publish({ entityAction: EntityAction.AttachOnImport, entity: targetEntity });
        if (!entityState.isUnchanged()) {
          em._notifyStateChange(targetEntity, true);
        }

      }

      entitiesToLink.push(targetEntity);
    });
    return entitiesToLink;
  }

  function getMappedKey(tempKeyMap, entityKey) {
    var newKey = tempKeyMap[entityKey.toString()];
    if (newKey) return newKey;
    var subtypes = entityKey._subtypes;
    if (!subtypes) return null;
    for (var i = 0, j = subtypes.length; i < j; i++) {
      newKey = tempKeyMap[entityKey.toString(subtypes[i])];
      if (newKey) return newKey;
    }
    return null;
  }

  function promiseWithCallbacks(promise, callback, errorCallback) {
    promise = promise.then(function (data) {
      if (callback) callback(data);
      return Q.resolve(data);
    }, function (error) {
      if (errorCallback) errorCallback(error);
      return Q.reject(error);
    });
    return promise;
  }

  function getEntitiesToSave(em, entities) {
    var entitiesToSave;
    if (entities) {
      entitiesToSave = entities.filter(function (e) {
        if (e.entityAspect.entityManager !== em) {
          throw new Error("Only entities in this entityManager may be saved");
        }
        return !e.entityAspect.entityState.isDetached();
      });
    } else {
      entitiesToSave = em.getChanges();
    }
    return entitiesToSave;
  }

  function fixupKeys(em, keyMappings) {
    em._inKeyFixup = true;
    keyMappings.forEach(function (km) {
      var group = em._entityGroupMap[km.entityTypeName];
      if (!group) {
        throw new Error("Unable to locate the following fully qualified EntityType name: " + km.entityTypeName);
      }
      group._fixupKey(km.tempValue, km.realValue);
    });
    em._inKeyFixup = false;
  }

  function getEntityGroups(em, entityTypes) {
    var groupMap = em._entityGroupMap;
    if (entityTypes) {
      return __toArray(entityTypes).map(function (et) {
        if (et instanceof EntityType) {
          return groupMap[et.name];
        } else {
          throw new Error("The EntityManager.getChanges() 'entityTypes' parameter must be either an entityType or an array of entityTypes or null");
        }
      });
    } else {
      return __getOwnPropertyValues(groupMap);
    }
  }

  function checkEntityKey(em, entity) {
    var ek = entity.entityAspect.getKey();
    // return properties that are = to defaultValues
    var keyPropsWithDefaultValues = __arrayZip(entity.entityType.keyProperties, ek.values, function (kp, kv) {
      return (kp.defaultValue === kv) ? kp : null;
    }).filter(function (kp) {
      return kp !== null;
    });
    if (keyPropsWithDefaultValues.length) {
      if (entity.entityType.autoGeneratedKeyType !== AutoGeneratedKeyType.None) {
        em.generateTempKeyValue(entity);
      } else {
        // we will allow attaches of entities where only part of the key is set.
        if (keyPropsWithDefaultValues.length === ek.values.length) {
          throw new Error("Cannot attach an object of type  (" + entity.entityType.name + ") to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'");
        }
      }
    }
  }

  function validateEntityStates(em, entityStates) {
    if (!entityStates) return null;
    entityStates = __toArray(entityStates);
    entityStates.forEach(function (es) {
      if (!EntityState.contains(es)) {
        throw new Error("The EntityManager.getChanges() 'entityStates' parameter must either be null, an entityState or an array of entityStates");
      }
    });
    return entityStates;
  }

  proto._attachEntityCore = function (entity, entityState, mergeStrategy) {
    var group = findOrCreateEntityGroup(this, entity.entityType);
    var attachedEntity = group.attachEntity(entity, entityState, mergeStrategy);
    this._linkRelatedEntities(attachedEntity);
    return attachedEntity;
  }

  proto._updateFkVal = function (fkProp, oldValue, newValue) {
    var group = this._entityGroupMap[fkProp.parentType.name];
    if (!group) return;
    group._updateFkVal(fkProp, oldValue, newValue);
  }

  function attachRelatedEntities(em, entity, entityState, mergeStrategy) {
    var navProps = entity.entityType.navigationProperties;
    navProps.forEach(function (np) {
      var related = entity.getProperty(np.name);
      if (np.isScalar) {
        if (!related) return;
        em.attachEntity(related, entityState, mergeStrategy);
      } else {
        related.forEach(function (e) {
          em.attachEntity(e, entityState, mergeStrategy);
        });
      }
    });
  }

  // returns a promise
  function executeQueryCore(em, query, queryOptions, dataService) {
    try {
      var results;
      var metadataStore = em.metadataStore;

      if (metadataStore.isEmpty() && dataService.hasServerMetadata) {
        throw new Error("cannot execute _executeQueryCore until metadataStore is populated.");
      }

      if (queryOptions.fetchStrategy === FetchStrategy.FromLocalCache) {
        try {
          var qr = executeQueryLocallyCore(em, query);
          return Q.resolve({ results: qr.results, entityManager: em, inlineCount: qr.inlineCount, query: query });
        } catch (e) {
          return Q.reject(e);
        }
      }

      var mappingContext = new MappingContext({
        query: query,
        entityManager: em,
        dataService: dataService,
        mergeOptions: {
          mergeStrategy: queryOptions.mergeStrategy,
          noTracking: !!query.noTrackingEnabled,
          includeDeleted: queryOptions.includeDeleted
        }
      });

      var validateOnQuery = em.validationOptions.validateOnQuery;

      return dataService.adapterInstance.executeQuery(mappingContext).then(function (data) {
        var result = __wrapExecution(function () {
          var state = { isLoading: em.isLoading };
          em.isLoading = true;
          em._pendingPubs = [];
          return state;
        }, function (state) {
          // cleanup
          em.isLoading = state.isLoading;
          em._pendingPubs.forEach(function (fn) {
            fn();
          });
          em._pendingPubs = null;
          em._hasChangesAction && em._hasChangesAction();
          // HACK for GC
          query = null;
          mappingContext = null;
          // HACK: some errors thrown in next function do not propogate properly - this catches them.

          if (state.error) {
            Q.reject(state.error);
          }

        }, function () {
          var nodes = dataService.jsonResultsAdapter.extractResults(data);
          nodes = __toArray(nodes);

          results = mappingContext.visitAndMerge(nodes, { nodeType: "root" });
          if (validateOnQuery) {
            results.forEach(function (r) {
              // anon types and simple types will not have an entityAspect.
              r.entityAspect && r.entityAspect.validateEntity();
            });
          }
          mappingContext.processDeferred();
          // if query has expand clauses walk each of the 'results' and mark the expanded props as loaded.
          markLoadedNavProps(results, query);
          var retrievedEntities = __objectMap(mappingContext.refMap);
          return { results: results, query: query, entityManager: em, httpResponse: data.httpResponse, inlineCount: data.inlineCount, retrievedEntities: retrievedEntities };
        });
        return Q.resolve(result);
      }, function (e) {
        if (e) {
          e.query = query;
          e.entityManager = em;
        }
        return Q.reject(e);
      });

    } catch (e) {
      if (e) {
        e.query = query;
      }
      return Q.reject(e);
    }
  }

  function markLoadedNavProps(entities, query) {
    if (query.noTrackingEnabled) return;
    var expandClause = query.expandClause;
    if (expandClause == null) return;
    expandClause.propertyPaths.forEach(function (propertyPath) {
      var propNames = propertyPath.split('.');
      markLoadedNavPath(entities, propNames);
    });
  }

  function markLoadedNavPath(entities, propNames) {
    var propName = propNames[0];
    entities.forEach(function (entity) {
      var ea = entity.entityAspect;
      if (!ea) return; // entity may not be a 'real' entity in the case of a projection.
      ea._markAsLoaded(propName);
      if (propNames.length === 1) return;
      var next = entity.getProperty(propName);
      if (!next) return; // no children to process.
      // strange logic because nonscalar nav values are NOT really arrays
      // otherwise we could use Array.isArray
      if (!next.arrayChanged) next = [next];
      markLoadedNavPath(next, propNames.slice(1));
    });
  }

  function updateConcurrencyProperties(entities) {
    var candidates = entities.filter(function (e) {
      e.entityAspect.isBeingSaved = true;
      return e.entityAspect.entityState.isModified()
          && e.entityType.concurrencyProperties.length > 0;

    });
    if (candidates.length === 0) return;
    candidates.forEach(function (c) {
      c.entityType.concurrencyProperties.forEach(function (cp) {
        updateConcurrencyProperty(c, cp);
      });
    });
  }

  function updateConcurrencyProperty(entity, property) {
    // check if property has already been updated
    if (entity.entityAspect.originalValues[property.name]) return;
    var value = entity.getProperty(property.name);
    if (!value) value = property.dataType.defaultValue;
    if (property.dataType.isNumeric) {
      entity.setProperty(property.name, value + 1);
    } else if (property.dataType.getConcurrencyValue) {
      // DataType has its own implementation
      var nextValue = property.dataType.getConcurrencyValue(value);
      entity.setProperty(property.name, nextValue);
    } else if (property.dataType === DataType.Binary) {
      // best guess - that this is a timestamp column and is computed on the server during save
      // - so no need to set it here.
      return;
    } else {
      // this just leaves DataTypes of Boolean, String and Byte - none of which should be the
      // type for a concurrency column.
      // NOTE: thought about just returning here but would rather be safe for now.
      throw new Error("Unable to update the value of concurrency property before saving: " + property.name);
    }
  }


  function findOrCreateEntityGroup(em, entityType) {
    var group = em._entityGroupMap[entityType.name];
    if (!group) {
      group = new EntityGroup(em, entityType);
      em._entityGroupMap[entityType.name] = group;
    }
    return group;
  }

  function findOrCreateEntityGroups(em, entityType) {
    var entityTypes = entityType.getSelfAndSubtypes();
    return entityTypes.map(function (et) {
      return findOrCreateEntityGroup(em, et);
    });
  }


  proto.helper = {
    unwrapInstance: unwrapInstance,
    unwrapOriginalValues: unwrapOriginalValues,
    unwrapChangedValues: unwrapChangedValues
  };


  function unwrapInstance(structObj, transformFn) {

    var rawObject = {};
    var stype = structObj.entityType || structObj.complexType;
    var serializerFn = getSerializerFn(stype);
    var unmapped = {};
    stype.dataProperties.forEach(function (dp) {
      if (dp.isComplexProperty) {
        rawObject[dp.nameOnServer] = __map(structObj.getProperty(dp.name), function (co) {
          return unwrapInstance(co, transformFn);
        });
      } else {
        var val = structObj.getProperty(dp.name);
        val = transformFn ? transformFn(dp, val) : val;
        if (val === undefined) return;
        val = serializerFn ? serializerFn(dp, val) : val;
        if (val !== undefined) {
          if (dp.isUnmapped) {
            unmapped[dp.nameOnServer] = __toJSONSafe(val);
          } else {
            rawObject[dp.nameOnServer] = val;
          }
        }
      }
    });

    if (!__isEmpty(unmapped)) {
      rawObject.__unmapped = unmapped;
    }
    return rawObject;
  }

  function unwrapOriginalValues(target, metadataStore, transformFn) {
    var stype = target.entityType || target.complexType;
    var aspect = target.entityAspect || target.complexAspect;
    var fn = metadataStore.namingConvention.clientPropertyNameToServer;
    var result = {};
    __objectForEach(aspect.originalValues, function (propName, val) {
      var prop = stype.getProperty(propName);
      val = transformFn ? transformFn(prop, val) : val;
      if (val !== undefined) {
        result[fn(propName, prop)] = val;
      }
    });
    stype.complexProperties.forEach(function (cp) {
      var nextTarget = target.getProperty(cp.name);
      if (cp.isScalar) {
        var unwrappedCo = unwrapOriginalValues(nextTarget, metadataStore, transformFn);
        if (!__isEmpty(unwrappedCo)) {
          result[fn(cp.name, cp)] = unwrappedCo;
        }
      } else {
        var unwrappedCos = nextTarget.map(function (item) {
          return unwrapOriginalValues(item, metadataStore, transformFn);
        });
        result[fn(cp.name, cp)] = unwrappedCos;
      }
    });
    return result;
  }

  function unwrapChangedValues(entity, metadataStore, transformFn) {
    var stype = entity.entityType;
    var serializerFn = getSerializerFn(stype);
    var fn = metadataStore.namingConvention.clientPropertyNameToServer;
    var result = {};
    __objectForEach(entity.entityAspect.originalValues, function (propName, value) {
      var prop = stype.getProperty(propName);
      var val = entity.getProperty(propName);
      val = transformFn ? transformFn(prop, val) : val;
      if (val === undefined) return;
      val = serializerFn ? serializerFn(prop, val) : val;
      if (val !== undefined) {
        result[fn(propName, prop)] = val;
      }
    });
    // any change to any complex object or array of complex objects returns the ENTIRE
    // current complex object or complex object array.  This is by design. Complex Objects
    // are atomic.
    stype.complexProperties.forEach(function (cp) {
      if (cpHasOriginalValues(entity, cp)) {
        var coOrCos = entity.getProperty(cp.name);
        result[fn(cp.name, cp)] = __map(coOrCos, function (co) {
          return unwrapInstance(co, transformFn);
        });
      }
    });
    return result;
  }

  function cpHasOriginalValues(structuralObject, cp) {
    var coOrCos = structuralObject.getProperty(cp.name);
    if (cp.isScalar) {
      return coHasOriginalValues(coOrCos);
    } else {
      // this occurs when a nonscalar co array has had cos added or removed.
      if (coOrCos._origValues) return true;
      return coOrCos.some(function (co) {
        return coHasOriginalValues(co);
      });
    }
  }

  function coHasOriginalValues(co) {
    // next line checks all non complex properties of the co.
    if (!__isEmpty(co.complexAspect.originalValues)) return true;
    // now need to recursively check each of the cps
    return co.complexType.complexProperties.some(function (cp) {
      return cpHasOriginalValues(co, cp);
    });
  }

  function getSerializerFn(stype) {
    return stype.serializerFn || (stype.metadataStore && stype.metadataStore.serializerFn);
  }


  function UnattachedChildrenMap() {
    // key is EntityKey.toString(), value is array of { navigationProperty, children }
    this.map = {};
  }

  UnattachedChildrenMap.prototype.addChild = function (parentEntityKey, navigationProperty, child) {
    var tuple = this.getTuple(parentEntityKey, navigationProperty);
    if (!tuple) {
      tuple = { navigationProperty: navigationProperty, children: [] };
      __getArray(this.map, parentEntityKey.toString()).push(tuple);
    }
    tuple.children.push(child);
  };

  // UnattachedChildrenMap.prototype.removeChildren = function (parentEntityKey, navigationProperty) {
  //   var tuples = this.getTuples(parentEntityKey);
  //   if (!tuples) return;
  //   __arrayRemoveItem(tuples, function (t) {
  //     return t.navigationProperty === navigationProperty;
  //   });
  //   if (!tuples.length) {
  //     delete this.map[parentEntityKey.toString()];
  //   }
  // };

  UnattachedChildrenMap.prototype.removeChildrenByString = function (parentEntityKeyString, navigationProperty) {
    var tuples = this.map[parentEntityKeyString];
    if (!tuples) return;
    __arrayRemoveItem(tuples, function (t) {
      return t.navigationProperty === navigationProperty;
    });
    if (!tuples.length) {
      delete this.map[parentEntityKeyString];
    }
  };

  // UnattachedChildrenMap.prototype.getChildren = function (parentEntityKey, navigationProperty) {
  //   var tuple = this.getTuple(parentEntityKey, navigationProperty);
  //   if (tuple) {
  //     return tuple.children.filter(function (child) {
  //       // it may have later been detached.
  //       return !child.entityAspect.entityState.isDetached();
  //     });
  //   } else {
  //     return null;
  //   }
  // };

  UnattachedChildrenMap.prototype.getTuple = function (parentEntityKey, navigationProperty) {
    var tuples = this.getTuples(parentEntityKey);
    if (!tuples) return null;
    var tuple = __arrayFirst(tuples, function (t) {
      return t.navigationProperty === navigationProperty;
    });
    return tuple;
  };


  UnattachedChildrenMap.prototype.getTuples = function (parentEntityKey) {
    var allTuples = [];
    var tuples = this.map[parentEntityKey.toString()];
    if (tuples) {
      allTuples = allTuples.concat(tuples);
    }
    var entityType = parentEntityKey.entityType;
    while (entityType.baseEntityType) {
      entityType = entityType.baseEntityType;
      var baseKey = parentEntityKey.toString(entityType);
      tuples = this.map[baseKey];
      if (tuples) {
        allTuples = allTuples.concat(tuples);
      }
    }
    return (allTuples.length) ? allTuples : undefined;
  };

  UnattachedChildrenMap.prototype.getTuplesByString = function (parentEntityKeyString) {
    return this.map[parentEntityKeyString];
  };

  return ctor;
})();


// expose
breeze.EntityManager = EntityManager;

;/**
 @module breeze
 **/

// Internal helper class

var MappingContext = (function () {

  var ctor = function MappingContext(config) {

    __extend(this, config, [
      "query", "entityManager", "dataService", "mergeOptions"
    ]);

    // calc'd props
    this.refMap = {};
    this.deferredFns = [];
    this.jsonResultsAdapter = this.dataService.jsonResultsAdapter;
    this.metadataStore = this.entityManager.metadataStore;
    this.rawValueFn = DataProperty.getRawValueFromServer; // think about passing this in later.
  };

  var proto = ctor.prototype;
  var parseRawValue = DataType.parseRawValue;
  proto._$typeName = "MappingContext";

  proto.getUrl = function () {
    var query = this.query;
    if (!query) {
      throw new Error("query cannot be empty");
    }
    var uriString;
    if (typeof query === 'string') {
      uriString = query;
    } else if (query instanceof EntityQuery) {
      uriString = this.dataService.uriBuilder.buildUri(query, this.metadataStore);
    } else {
      throw new Error("unable to recognize query parameter as either a string or an EntityQuery");
    }
    return  this.dataService.qualifyUrl(uriString);
  }

  proto.visitAndMerge = function (nodes, nodeContext) {
    var query = this.query;
    var jra = this.jsonResultsAdapter;
    nodeContext = nodeContext || {};
    var that = this;
    return __map(nodes, function (node) {
      if (query == null && node.entityAspect) {
        // don't bother merging a result from a save that was not returned from the server.
        if (node.entityAspect.entityState.isDeleted()) {
          that.entityManager.detachEntity(node);
        } else {
          node.entityAspect.acceptChanges();
        }
        return node;
      }

      var meta = jra.visitNode(node, that, nodeContext) || {};
      node = meta.node || node;
      if (query && nodeContext.nodeType === "root" && !meta.entityType) {
        meta.entityType = query._getToEntityType && query._getToEntityType(that.metadataStore);
      }
      return processMeta(that, node, meta);
    }, this.mergeOptions.includeDeleted);
  };

  proto.processDeferred = function () {
    if (this.deferredFns.length > 0) {
      this.deferredFns.forEach(function (fn) {
        fn();
      });
    }
  }

  function processMeta(mc, node, meta, assignFn) {
    // == is deliberate here instead of ===
    if (meta.ignore || node == null) {
      return null;
    } else if (meta.nodeRefId) {
      var refValue = resolveEntityRef(mc, meta.nodeRefId);
      if (typeof refValue === "function" && assignFn != null) {
        mc.deferredFns.push(function () {
          assignFn(refValue);
        });
        return undefined; // deferred and will be set later;
      }
      return refValue;
    } else if (meta.entityType) {
      var entityType = meta.entityType;
      if (mc.mergeOptions.noTracking) {
        node = processNoMerge(mc, entityType, node);
        if (entityType.noTrackingFn) {
          node = entityType.noTrackingFn(node, entityType);
        }
        if (meta.nodeId) {
          mc.refMap[meta.nodeId] = node;
        }
        return node;
      } else {
        if (entityType.isComplexType) {
          // because we still need to do serverName to client name processing
          return processNoMerge(mc, entityType, node);
        } else {
          return mergeEntity(mc, node, meta);
        }
      }
    } else {

      if ((!meta.passThru) && typeof node === 'object' && !__isDate(node)) {
        node = processAnonType(mc, node);
      }

      // updating the refMap for entities is handled by updateEntityRef for entities.
      if (meta.nodeId) {
        mc.refMap[meta.nodeId] = node;
      }
      return node;
    }
  }

  function processNoMerge(mc, stype, node) {
    var result = {};

    stype.dataProperties.forEach(function (dp) {
      if (dp.isComplexProperty) {
        result[dp.name] = __map(node[dp.nameOnServer], function (v) {
          return processNoMerge(mc, dp.dataType, v);
        });
      } else {
        result[dp.name] = parseRawValue(node[dp.nameOnServer], dp.dataType);
      }
    });

    stype.navigationProperties && stype.navigationProperties.forEach(function (np) {
      var nodeContext = { nodeType: "navProp", navigationProperty: np };
      visitNode(node[np.nameOnServer], mc, nodeContext, result, np.name);
    });

    return result;
  }

  function processAnonType(mc, node) {
    // node is guaranteed to be an object by this point, i.e. not a scalar
    var keyFn = mc.metadataStore.namingConvention.serverPropertyNameToClient;
    var result = {};

    __objectForEach(node, function (key, value) {
      var newKey = keyFn(key);
      var nodeContext = { nodeType: "anonProp", propertyName: newKey };
      visitNode(value, mc, nodeContext, result, newKey);
    });
    return result;
  }

  function visitNode(node, mc, nodeContext, result, key) {
    var jra = mc.jsonResultsAdapter;
    var meta = jra.visitNode(node, mc, nodeContext) || {};
    // allows visitNode to change the value;
    node = meta.node || node;

    if (meta.ignore) return;
    if (meta.passThru) return node;
    if (Array.isArray(node)) {
      nodeContext.nodeType = nodeContext.nodeType + "Item";
      result[key] = node.map(function (v, ix) {
        meta = jra.visitNode(v, mc, nodeContext) || {};
        v = meta.node || v;
        return processMeta(mc, v, meta, function (refValue) {
          result[key][ix] = refValue();
        });
      });
    } else {
      result[key] = processMeta(mc, node, meta, function (refValue) {
        result[key] = refValue();
      });
    }
  }

  function resolveEntityRef(mc, nodeRefId) {
    var entity = mc.refMap[nodeRefId];
    if (entity === undefined) {
      return function () {
        return mc.refMap[nodeRefId];
      };
    } else {
      return entity;
    }
  }

  function updateEntityRef(mc, targetEntity, node) {
    var nodeId = node._$meta.nodeId;
    if (!nodeId && node._$meta.extraMetadata) {
      // odata case.  refMap isn't really used, but is returned as data.retrievedEntities, so we populated it anyway.
      nodeId = node._$meta.extraMetadata.uriKey;
    }
    if (nodeId != null) {
      mc.refMap[nodeId] = targetEntity;
    }
  }

  // can return null for a deleted entity if includeDeleted == false
  function mergeEntity(mc, node, meta) {
    node._$meta = meta;
    var em = mc.entityManager;

    var entityType = meta.entityType;
    if (typeof (entityType) === 'string') {
      entityType = mc.metadataStore._getEntityType(entityType, false);
    }
    node.entityType = entityType;

    var mergeStrategy = mc.mergeOptions.mergeStrategy;
    var isSaving = mc.query == null;

    var entityKey = entityType.getEntityKeyFromRawEntity(node, mc.rawValueFn);
    var targetEntity = em.findEntityByKey(entityKey);
    if (targetEntity) {
      if (isSaving && targetEntity.entityAspect.entityState.isDeleted()) {
        em.detachEntity(targetEntity);
        return targetEntity;
      }
      var targetEntityState = targetEntity.entityAspect.entityState;
      if (mergeStrategy === MergeStrategy.Disallowed) {
        throw new Error("A MergeStrategy of 'Disallowed' prevents " + entityKey.toString() + " from being merged");
      } else if (mergeStrategy === MergeStrategy.SkipMerge) {
        updateEntityNoMerge(mc, targetEntity, node);
      } else {
        if (mergeStrategy === MergeStrategy.OverwriteChanges
            || targetEntityState.isUnchanged()) {
          updateEntity(mc, targetEntity, node);
          targetEntity.entityAspect.wasLoaded = true;
          if (meta.extraMetadata) {
            targetEntity.entityAspect.extraMetadata = meta.extraMetadata;
          }
          targetEntity.entityAspect.entityState = EntityState.Unchanged;
          clearOriginalValues(targetEntity);
          targetEntity.entityAspect.propertyChanged.publish({ entity: targetEntity, propertyName: null });
          var action = isSaving ? EntityAction.MergeOnSave : EntityAction.MergeOnQuery;
          em.entityChanged.publish({ entityAction: action, entity: targetEntity });
          // this is needed to handle an overwrite of a modified entity with an unchanged entity
          // which might in turn cause _hasChanges to change.
          if (!targetEntityState.isUnchanged()) {
            em._notifyStateChange(targetEntity, false);
          }
        } else {
          if (targetEntityState == EntityState.Deleted && !mc.mergeOptions.includeDeleted) {
            return null;
          }
          updateEntityNoMerge(mc, targetEntity, node);
        }
      }
    } else {
      targetEntity = entityType._createInstanceCore();

      updateEntity(mc, targetEntity, node);

      if (meta.extraMetadata) {
        targetEntity.entityAspect.extraMetadata = meta.extraMetadata;
      }
      // em._attachEntityCore(targetEntity, EntityState.Unchanged, MergeStrategy.Disallowed);
      em._attachEntityCore(targetEntity, EntityState.Unchanged, mergeStrategy);
      targetEntity.entityAspect.wasLoaded = true;
      em.entityChanged.publish({ entityAction: EntityAction.AttachOnQuery, entity: targetEntity });
    }
    return targetEntity;
  }

  // copied from entityAspect
  function clearOriginalValues(target) {
    var aspect = target.entityAspect || target.complexAspect;
    aspect.originalValues = {};
    var stype = target.entityType || target.complexType;
    stype.complexProperties.forEach(function (cp) {
      var cos = target.getProperty(cp.name);
      if (cp.isScalar) {
        clearOriginalValues(cos);
      } else {
        cos._acceptChanges();
        cos.forEach(clearOriginalValues);
      }
    });
  }

  function updateEntityNoMerge(mc, targetEntity, node) {
    updateEntityRef(mc, targetEntity, node);
    // we still need to merge related entities even if top level entity wasn't modified.
    node.entityType.navigationProperties.forEach(function (np) {
      if (np.isScalar) {
        mergeRelatedEntityCore(mc, node, np);
      } else {
        mergeRelatedEntitiesCore(mc, node, np);
      }
    });
  }

  function updateEntity(mc, targetEntity, node) {
    updateEntityRef(mc, targetEntity, node);
    var entityType = targetEntity.entityType;
    entityType._updateTargetFromRaw(targetEntity, node, mc.rawValueFn);

    entityType.navigationProperties.forEach(function (np) {
      if (np.isScalar) {
        mergeRelatedEntity(mc, np, targetEntity, node);
      } else {
        mergeRelatedEntities(mc, np, targetEntity, node);
      }
    });
  }

  function mergeRelatedEntity(mc, navigationProperty, targetEntity, rawEntity) {

    var relatedEntity = mergeRelatedEntityCore(mc, rawEntity, navigationProperty);
    if (relatedEntity == null) return;
    if (typeof relatedEntity === 'function') {
      mc.deferredFns.push(function () {
        relatedEntity = relatedEntity();
        updateRelatedEntity(relatedEntity, targetEntity, navigationProperty);
      });
    } else {
      updateRelatedEntity(relatedEntity, targetEntity, navigationProperty);
    }
  }

  function mergeRelatedEntities(mc, navigationProperty, targetEntity, rawEntity) {
    var relatedEntities = mergeRelatedEntitiesCore(mc, rawEntity, navigationProperty);
    if (relatedEntities == null) return;

    var inverseProperty = navigationProperty.getInverse();
    if (!inverseProperty) return;

    var originalRelatedEntities = targetEntity.getProperty(navigationProperty.name);
    originalRelatedEntities.wasLoaded = true;

    relatedEntities.forEach(function (relatedEntity) {
      if (typeof relatedEntity === 'function') {
        mc.deferredFns.push(function () {
          relatedEntity = relatedEntity();
          updateRelatedEntityInCollection(mc, relatedEntity, originalRelatedEntities, targetEntity, inverseProperty);
        });
      } else {
        updateRelatedEntityInCollection(mc, relatedEntity, originalRelatedEntities, targetEntity, inverseProperty);
      }
    });
  }

  function mergeRelatedEntityCore(mc, rawEntity, navigationProperty) {
    var relatedRawEntity = rawEntity[navigationProperty.nameOnServer];
    if (!relatedRawEntity) return null;

    var relatedEntity = mc.visitAndMerge(relatedRawEntity, { nodeType: "navProp", navigationProperty: navigationProperty });
    return relatedEntity;
  }

  function mergeRelatedEntitiesCore(mc, rawEntity, navigationProperty) {
    var relatedRawEntities = rawEntity[navigationProperty.nameOnServer];
    if (!relatedRawEntities) return null;

    // needed if what is returned is not an array and we expect one - this happens with __deferred in OData.
    if (!Array.isArray(relatedRawEntities)) {
      // return null;
      relatedRawEntities = relatedRawEntities.results; // OData v3 will look like this with an expand
      if (!relatedRawEntities) {
        return null;
      }
    }

    var relatedEntities = mc.visitAndMerge(relatedRawEntities, { nodeType: "navPropItem", navigationProperty: navigationProperty });
    return relatedEntities;
  }

  function updateRelatedEntity(relatedEntity, targetEntity, navigationProperty) {
    if (!relatedEntity) return;
    var propName = navigationProperty.name;
    var currentRelatedEntity = targetEntity.getProperty(propName);

    // check if the related entity is already hooked up
    if (currentRelatedEntity !== relatedEntity) {
      // if not hook up both directions.
      targetEntity.setProperty(propName, relatedEntity);
      var inverseProperty = navigationProperty.getInverse();
      if (!inverseProperty) return;
      if (inverseProperty.isScalar) {
        relatedEntity.setProperty(inverseProperty.name, targetEntity);
      } else {
        var collection = relatedEntity.getProperty(inverseProperty.name);
        collection.push(targetEntity);

      }
    }
  }

  function updateRelatedEntityInCollection(mc, relatedEntity, relatedEntities, targetEntity, inverseProperty) {
    if (!relatedEntity) return;

    // don't update relatedCollection if preserveChanges & relatedEntity has an fkChange.
    if (relatedEntity.entityAspect.entityState === EntityState.Modified
      && mc.mergeOptions.mergeStrategy === MergeStrategy.PreserveChanges) {
      var origValues = relatedEntity.entityAspect.originalValues;
      var fkWasModified = inverseProperty.relatedDataProperties.some(function(dp) {
        return origValues[dp.name] != undefined;
      });
      if (fkWasModified) return;
    }
    // check if the related entity is already hooked up
    var thisEntity = relatedEntity.getProperty(inverseProperty.name);

    if (thisEntity !== targetEntity) {
      // if not - hook it up.
      relatedEntities.push(relatedEntity);
      relatedEntity.setProperty(inverseProperty.name, targetEntity);
    }
  }


  return ctor;
})();
   


;/**
@module breeze
**/

var SaveOptions = (function () {
  /**
  A SaveOptions instance is used to specify the 'options' under which a save will occur.

  @class SaveOptions
  **/

  /**
  @method <ctor> SaveOptions
  @param config {Object}
  @param [config.allowConcurrentSaves] {Boolean} Whether multiple saves can be in-flight at the same time. The default is false.
  @param [config.resourceName] {String} Resource name to be used during the save - this defaults to "SaveChanges"
  @param [config.dataService] {DataService} The DataService to be used for this save.
  @param [config.tag] {Object} Free form value that will be sent to the server during the save.
  **/
  var ctor = function SaveOptions(config) {
    updateWithConfig(this, config);
  };

  var proto = ctor.prototype;
  proto._$typeName = "SaveOptions";

  /**
  Sets the 'defaultInstance' by creating a copy of the current 'defaultInstance' and then applying all of the properties of the current instance.
  The current instance is returned unchanged.
  @method setAsDefault
  @chainable
  **/
  proto.setAsDefault = function () {
    return __setAsDefault(this, ctor);
  };

  /**
  Whether another save can be occuring at the same time as this one - default is false.

  __readOnly__
  @property allowConcurrentSaves {Boolean}
  **/

  /**
  A {{#crossLink "DataService"}}{{/crossLink}}.
  __readOnly__
  @property dataService {DataService}
  **/

  /**
  The resource name to call to perform the save.
  __readOnly__
  @property resourceName {String}
  **/

  /**
  A free form value that will be sent to the server.

  __readOnly__
  @property tag {Object}
  **/

  /**
  The default value whenever SaveOptions are not specified.
  @property defaultInstance {SaveOptions}
  @static
  **/

  /**
  Returns a copy of this SaveOptions with the specified config options applied.
  @example
      var saveOptions = em1.saveOptions.using( {resourceName: "anotherResource" });

  @method using
  @param config {Configuration Object|} The object to apply to create a new SaveOptions.
  @param [config.allowConcurrentSaves] {Boolean} Whether multiple saves can be in-flight at the same time. The default is false.
  @param [config.resourceName] {String} Resource name to be used during the save - this defaults to "SaveChanges"
  @param [config.dataService] {DataService} The DataService to be used for this save.
  @param [config.tag] {Object} Free form value that will be sent to the server during the save.
  @chainable
  **/
  proto.using = function (config) {
    return updateWithConfig(this, config);
  };

  function updateWithConfig(obj, config) {
    if (config) {
      assertConfig(config)
          .whereParam("resourceName").isOptional().isString()
          .whereParam("dataService").isOptional().isInstanceOf(DataService)
          .whereParam("allowConcurrentSaves").isBoolean().isOptional()
          .whereParam("tag").isOptional()
          .applyAll(obj);
    }
    return obj;
  }

  ctor.defaultInstance = new ctor({ allowConcurrentSaves: false});
  return ctor;
})();

breeze.SaveOptions = SaveOptions;


;breeze.AbstractDataServiceAdapter = (function () {

  var ajaxImpl;

  var ctor = function () {
  };

  var proto = ctor.prototype; // minifies better (as seen in jQuery)

  proto.checkForRecomposition = function (interfaceInitializedArgs) {
    if (interfaceInitializedArgs.interfaceName === "ajax" && interfaceInitializedArgs.isDefault) {
      this.initialize();
    }
  };

  proto.initialize = function () {
    ajaxImpl = breeze.config.getAdapterInstance("ajax");

    // don't cache 'ajax' because then we would need to ".bind" it, and don't want to because of brower support issues.
    if (ajaxImpl && ajaxImpl.ajax) {
      return;
    }
    throw new Error("Unable to find ajax adapter for dataservice adapter '" + (this.name || '') + "'.");
  };

  proto.fetchMetadata = function (metadataStore, dataService) {
    var serviceName = dataService.serviceName;
    var url = dataService.qualifyUrl("Metadata");

    var deferred = Q.defer();

    ajaxImpl.ajax({
      type: "GET",
      url: url,
      dataType: 'json',
      success: function (httpResponse) {

        // might have been fetched by another query
        if (metadataStore.hasMetadataFor(serviceName)) {
          return deferred.resolve("already fetched");
        }
        var data = httpResponse.data;
        try {
          var metadata = typeof (data) === "string" ? JSON.parse(data) : data;
          metadataStore.importMetadata(metadata);
        } catch (e) {
          var errMsg = "Unable to either parse or import metadata: " + e.message;
          return handleHttpError(deferred, httpResponse, "Metadata query failed for: " + url + ". " + errMsg);
        }

        // import may have brought in the service.
        if (!metadataStore.hasMetadataFor(serviceName)) {
          metadataStore.addDataService(dataService);
        }

        return deferred.resolve(metadata);

      },
      error: function (httpResponse) {
        handleHttpError(deferred, httpResponse, "Metadata query failed for: " + url);
      }
    });
    return deferred.promise;
  };

  proto.executeQuery = function (mappingContext) {
    var adapter = mappingContext.adapter = this;
    var deferred = Q.defer();
    var url = mappingContext.getUrl();

    var params = {
      type: "GET",
      url: url,
      params: mappingContext.query.parameters,
      dataType: 'json',
      success: function (httpResponse) {
        var data = httpResponse.data;
        try {
          var rData;
          var results = data && (data.results || data.Results);
          if (results) {
            rData = { results: results, inlineCount: data.inlineCount || data.InlineCount, httpResponse: httpResponse };
          } else {
            rData = { results: data, httpResponse: httpResponse };
          }

          deferred.resolve(rData);
        } catch (e) {
          if (e instanceof Error) {
            deferred.reject(e);
          } else {
            handleHttpError(deferred, httpResponse);
          }
        }

      },
      error: function (httpResponse) {
        handleHttpError(deferred, httpResponse);
      }
    };
    if (mappingContext.dataService.useJsonp) {
      params.dataType = 'jsonp';
      params.crossDomain = true;
    }
    ajaxImpl.ajax(params);
    return deferred.promise;
  };

  proto.saveChanges = function (saveContext, saveBundle) {
    var adapter = saveContext.adapter = this;
    var deferred = Q.defer();
    saveBundle = adapter._prepareSaveBundle(saveContext, saveBundle);
    var bundle = JSON.stringify(saveBundle);

    var url = saveContext.dataService.qualifyUrl(saveContext.resourceName);

    ajaxImpl.ajax({
      type: "POST",
      url: url,
      dataType: 'json',
      contentType: "application/json",
      data: bundle,
      success: function (httpResponse) {
        httpResponse.saveContext = saveContext;
        var data = httpResponse.data;
        if (data.Errors || data.errors) {
          handleHttpError(deferred, httpResponse);
        } else {
          var saveResult = adapter._prepareSaveResult(saveContext, data);
          saveResult.httpResponse = httpResponse;
          deferred.resolve(saveResult);
        }
      },
      error: function (httpResponse) {
        httpResponse.saveContext = saveContext;
        handleHttpError(deferred, httpResponse);
      }
    });

    return deferred.promise;
  };

  proto._prepareSaveBundle = function (/*saveContext, saveBundle*/) {
    // The implementor should call _createChangeRequestInterceptor
    throw new Error("Need a concrete implementation of _prepareSaveBundle");
  };

  /**
   Returns a constructor function for a "ChangeRequestInterceptor"
   that can tweak the saveBundle both as it is built and when it is completed
   by a concrete DataServiceAdapater.

   Initialized with a default, no-op implementation that developers can replace with a
   substantive implementation that changes the individual entity change requests
   or aspects of the entire 'saveBundle' without having to write their own DataService adapters.

   @example
   var adapter = breeze.config.getAdapterInstance('dataService');
   adapter.changeRequestInterceptor = function (saveContext, saveBundle) {
        this.getRequest = function (request, entity, index) {
            // alter the request that the adapter prepared for this entity
            // based on the entity, saveContext, and saveBundle
            // e.g., add a custom header or prune the originalValuesMap
            return request;
        };
        this.done = function (requests) {
            // alter the array of requests representing the entire change-set
            // based on the saveContext and saveBundle
        };
    }
   @method changeRequestInterceptor
   @param saveContext {Object} The BreezeJS "context" for the save operation.
   @param saveBundle {Object} Contains the array of entities-to-be-saved (AKA, the entity change-set).
   @return {Function} Constructor for a "ChangeRequestInterceptor".
   **/
  proto.changeRequestInterceptor = DefaultChangeRequestInterceptor;

  //This is a default, no-op implementation that developers can replace.
  function DefaultChangeRequestInterceptor(saveContext, saveBundle) {
    /**
     Prepare and return the save data for an entity change-set.

     The adapter calls this method for each entity in the change-set,
     after it has prepared a "change request" for that object.

     The method can do anything to the request but it must return a valid, non-null request.
     @example
     this.getRequest = function (request, entity, index) {
            // alter the request that the adapter prepared for this entity
            // based on the entity, saveContext, and saveBundle
            // e.g., add a custom header or prune the originalValuesMap
            return request;
        };
     @method getRequest
     @param request {Object} The object representing the adapter's request to save this entity.
     @param entity {Entity} The entity-to-be-save as it is in cache
     @param index {Integer} The zero-based index of this entity in the change-set array
     @return {Function} The potentially revised request.
     **/
    this.getRequest = function (request, entity, index) {
      return request;
    };

    /**
     Last chance to change anything about the 'requests' array
     after it has been built with requests for all of the entities-to-be-saved.

     The 'requests' array is the same as 'saveBundle.entities' in many implementations

     This method can do anything to the array including add and remove requests.
     It's up to you to ensure that server will accept the requests array data as valid.

     Returned value is ignored.
     @example
     this.done = function (requests) {
            // alter the array of requests representing the entire change-set
            // based on the saveContext and saveBundle
        };
     @method done
     @param requests {Array of Object} The adapter's array of request for this changeset.
     **/
    this.done = function (requests) {
    };
  }

  proto._createChangeRequestInterceptor = function (saveContext, saveBundle) {
    var adapter = saveContext.adapter;
    var cri = adapter.changeRequestInterceptor;
    var isFn = __isFunction;

    if (isFn(cri)) {
      var pre = adapter.name + " DataServiceAdapter's ChangeRequestInterceptor";
      var post = " is missing or not a function.";
      var interceptor = new cri(saveContext, saveBundle);
      if (!isFn(interceptor.getRequest)) {
        throw new Error(pre + '.getRequest' + post);
      }
      if (!isFn(interceptor.done)) {
        throw new Error(pre + '.done' + post);
      }
      return interceptor;
    } else {
      return new DefaultChangeRequestInterceptor(saveContext, saveBundle);
    }
  }

  proto._prepareSaveResult = function (/* saveContext, data */) {
    throw new Error("Need a concrete implementation of _prepareSaveResult");
  };

  proto.jsonResultsAdapter = new JsonResultsAdapter({
    name: "noop",

    visitNode: function (/* node, mappingContext, nodeContext */) {
      return {};
    }

  });

  function handleHttpError(deferred, httpResponse, messagePrefix) {
    var err = createError(httpResponse);
    proto._catchNoConnectionError(err);
    if (messagePrefix) {
      err.message = messagePrefix + "; " + err.message;
    }
    return deferred.reject(err);
  }


  function createError(httpResponse) {
    var err = new Error();
    err.httpResponse = httpResponse;
    err.status = httpResponse.status;

    var errObj = httpResponse.data;

    if (!errObj) {
      err.message = httpResponse.error && httpResponse.error.toString();
      return err;
    }

    // some ajax providers will convert errant result into an object ( angular), others will not (jQuery)
    // if not do it here.
    if (typeof errObj === "string") {
      try {
        errObj = JSON.parse(errObj);
      } catch (e) {
        // sometimes httpResponse.data is just the error message itself
        err.message = errObj;
        return err;
      }
    }

    var saveContext = httpResponse.saveContext;

    // if any of the follow properties exist the source is .NET
    var tmp = errObj.Message || errObj.ExceptionMessage || errObj.EntityErrors || errObj.Errors;
    var isDotNet = !!tmp;
    var message, entityErrors;
    if (!isDotNet) {
      message = errObj.message;
      entityErrors = errObj.errors || errObj.entityErrors;
    } else {
      var tmp = errObj;
      do {
        // .NET exceptions can provide both ExceptionMessage and Message but ExceptionMethod if it
        // exists has a more detailed message.
        message = tmp.ExceptionMessage || tmp.Message;
        tmp = tmp.InnerException;
      } while (tmp);
      // .EntityErrors will only occur as a result of an EntityErrorsException being deliberately thrown on the server
      entityErrors = errObj.Errors || errObj.EntityErrors;
      entityErrors = entityErrors && entityErrors.map(function (e) {
        return {
          errorName: e.ErrorName,
          entityTypeName: MetadataStore.normalizeTypeName(e.EntityTypeName),
          keyValues: e.KeyValues,
          propertyName: e.PropertyName,
          errorMessage: e.ErrorMessage
        };
      });
    }

    if (saveContext && entityErrors) {
      var propNameFn = saveContext.entityManager.metadataStore.namingConvention.serverPropertyNameToClient;
      entityErrors.forEach(function (e) {
        e.propertyName = e.propertyName && propNameFn(e.propertyName);
      });
      err.entityErrors = entityErrors
    }

    err.message = message || "Server side errors encountered - see the entityErrors collection on this object for more detail";
    return err;
  }

  // Put this at the bottom of your http error analysis
  proto._catchNoConnectionError = function (err) {
    if (err.status == 0 && err.message == null) {
      err.message = "HTTP response status 0 and no message.  " +
          "Likely did not or could not reach server. Is the server running?";
    }
  }

  return ctor;

})();
;// Angular ajax adapter
// See https://docs.angularjs.org/api/ng/service/$http
(function (factory) {
  // Module systems magic dance.
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";
  var core = breeze.core;

  var ctor = function AjaxAngularAdapter() {
    this.name = "angular";
    this.defaultSettings = { };
    this.requestInterceptor = null;
    // Will set:
    //   this.$http;
    //   this.$rootScope;
  };
  var proto = ctor.prototype;

  proto.initialize = function () {

    var ng = core.requireLib("angular");
    if (ng) {
      var $injector = ng.injector(['ng']);
      var http, rootScope;
      $injector.invoke(['$http', '$rootScope', function ($http, $rootScope) {
        http = $http;
        rootScope = $rootScope;
      }]);
      this.$http = http;
      this.$rootScope = rootScope;
    }

  };

  proto.setHttp = function (http) {
    this.$http = http;
    this.$rootScope = null; // to suppress $rootScope.digest
  };


  proto.ajax = function (config) {
    if (!this.$http) {
      throw new Error("Unable to locate angular for ajax adapter");
    }
    var ngConfig = {
      method: config.type,
      url: config.url,
      dataType: config.dataType,
      contentType: config.contentType,
      crossDomain: config.crossDomain,
      headers: config.headers || {}
    }

    if (config.params) {
      // Hack: because of the way that Angular handles writing parameters out to the url.
      // so this approach takes over the url param writing completely.
      // See: http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/
      var delim = (ngConfig.url.indexOf("?") >= 0) ? "&" : "?";
      ngConfig.url = ngConfig.url + delim + encodeParams(config.params);
    }

    if (config.data) {
      ngConfig.data = config.data;
    }

    if (!core.isEmpty(this.defaultSettings)) {
      var compositeConfig = core.extend({}, this.defaultSettings);
      ngConfig = core.extend(compositeConfig, ngConfig);
      // extend is shallow; extend headers separately
      var headers =core.extend({}, this.defaultSettings.headers); // copy default headers 1st
      ngConfig.headers = core.extend(headers, ngConfig.headers);
    }

    var requestInfo = {
      adapter: this,      // this adapter
      config: ngConfig,   // angular's $http configuration object
      dsaConfig: config,  // the config arg from the calling Breeze DataServiceAdapter
      success: successFn, // adapter's success callback
      error: errorFn,     // adapter's error callback
      responseSuccess: responseSuccessFn, // adapter's success callback (ng 1.6+)
      responseError: responseErrorFn      // adapter's error callback (ng 1.6+)
    }

    if (core.isFunction(this.requestInterceptor)) {
      this.requestInterceptor(requestInfo);
      if (this.requestInterceptor.oneTime) {
        this.requestInterceptor = null;
      }
    }

    if (requestInfo.config) { // exists unless requestInterceptor killed it.
      var prom = this.$http(requestInfo.config);
      if (prom.success) {
        // response for ng < 1.6        
        prom.success(requestInfo.success).error(requestInfo.error);
      } else {
        // response for ng 1.6+
        prom.then(requestInfo.responseSuccess).catch(requestInfo.responseError);
      }
      this.$rootScope && this.$rootScope.$digest();
    }

    function responseSuccessFn(response) {
      return successFn(response.data, response.status, response.headers, response.config, response.statusText);
    }

    function successFn(data, status, headers, xconfig, statusText) {
      // HACK: because $http returns a server side null as a string containing "null" - this is WRONG.
      if (data === "null") data = null;
      var httpResponse = {
        config: config,
        data: data,
        getHeaders: headers,
        ngConfig: xconfig,
        status: status,
        statusText: statusText
      };
      config.success(httpResponse);
    }

    function responseErrorFn(response) {
      return errorFn(response.data, response.status, response.headers, response.config, response.statusText);
    }

    function errorFn(data, status, headers, xconfig, statusText) {
      // Timeout appears as an error with status===0 and no data.
      // Make it better
      if (status === 0 && data == null) {
        data = 'timeout';
      }
      var httpResponse = {
        config: config,
        data: data,
        getHeaders: headers,
        ngConfig: xconfig,
        status: status,
        statusText: statusText
      };
      config.error(httpResponse);
    }
  };

  function encodeParams(obj) {
    var query = '';
    var subValue, innerObj, fullSubName;

    for (var name in obj) {
      var value = obj[name];

      if (value instanceof Array) {
        for (var i = 0; i < value.length; ++i) {
          subValue = value[i];
          fullSubName = name + '[' + i + ']';
          innerObj = {};
          innerObj[fullSubName] = subValue;
          query += encodeParams(innerObj) + '&';
        }
      } else if (value && value.toISOString) { // a feature of Date-like things
        query += encodeURIComponent(name) + '=' + encodeURIComponent(value.toISOString()) + '&';
      } else if (value instanceof Object) {
        for (var subName in value) {
          subValue = value[subName];
          fullSubName = name + '[' + subName + ']';
          innerObj = {};
          innerObj[fullSubName] = subValue;
          query += encodeParams(innerObj) + '&';
        }
      } else if (value === null) {
        query += encodeURIComponent(name) + '=&';
      } else if (value !== undefined) {
        query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
      }
    }

    return query.length ? query.substr(0, query.length - 1) : query;
  }

  breeze.config.registerAdapter("ajax", ctor);

}));
;// jQuery ajax adapter ( JQuery v.>=1.5 )
// see https://api.jquery.com/jQuery.ajax/
(function (factory) {
  // Module systems magic dance.
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";
  var core = breeze.core;

  var jQuery;

  var ctor = function AjaxJQueryAdapter() {
    this.name = "jQuery";
    this.defaultSettings = { };
    this.requestInterceptor = null;
  };
  var proto = ctor.prototype;

  proto.initialize = function () {
    // look for the jQuery lib but don't fail immediately if not found
    jQuery = core.requireLib("jQuery;jquery");
  };

  proto.ajax = function (config) {
    if (!jQuery) {
      throw new Error("Unable to locate jQuery");
    }
    var jqConfig = {
      type: config.type,
      url: config.url,
      data: config.params || config.data,
      dataType: config.dataType,
      contentType: config.contentType,
      crossDomain: config.crossDomain,
      headers: config.headers || {}
    }

    if (!core.isEmpty(this.defaultSettings)) {
      var compositeConfig = core.extend({}, this.defaultSettings);
      jqConfig = core.extend(compositeConfig, jqConfig);
      // extend is shallow; extend headers separately
      var headers =core.extend({}, this.defaultSettings.headers); // copy default headers 1st
      jqConfig.headers = core.extend(headers, jqConfig.headers);
    }

    var requestInfo = {
      adapter: this,      // this adapter
      config: jqConfig,   // jQuery's ajax 'settings' object
      dsaConfig: config,  // the config arg from the calling Breeze DataServiceAdapter
      success: successFn, // adapter's success callback
      error: errorFn      // adapter's error callback
    }

    if (core.isFunction(this.requestInterceptor)) {
      this.requestInterceptor(requestInfo);
      if (this.requestInterceptor.oneTime) {
        this.requestInterceptor = null;
      }
    }

    if (requestInfo.config) { // exists unless requestInterceptor killed it.
      requestInfo.jqXHR = jQuery.ajax(requestInfo.config)
          .done(requestInfo.success)
          .fail(requestInfo.error);
    }

    function successFn(data, statusText, jqXHR) {
      var httpResponse = {
        config: config,
        data: data,
        getHeaders: getHeadersFn(jqXHR),
        status: jqXHR.status,
        statusText: statusText
      };
      config.success(httpResponse);
      jqXHR.onreadystatechange = null;
      jqXHR.abort = null;
    }

    function errorFn(jqXHR, statusText, errorThrown) {
      var httpResponse = {
        config: config,
        data: jqXHR.responseText,
        error: errorThrown,
        getHeaders: getHeadersFn(jqXHR),
        status: jqXHR.status,
        statusText: statusText
      };
      config.error(httpResponse);
      jqXHR.onreadystatechange = null;
      jqXHR.abort = null;
    }
  };

  function getHeadersFn(jqXHR) {
    if (jqXHR.status === 0) { // timeout or abort; no headers
      return function (headerName) {
        return (headerName && headerName.length > 0) ? "" : {};
      };
    } else { // jqXHR should have header functions
      return function (headerName) {
        return (headerName && headerName.length > 0) ?
               jqXHR.getResponseHeader(headerName) :
               jqXHR.getAllResponseHeaders();
      };
    }
  }

  breeze.config.registerAdapter("ajax", ctor);

}));
;(function (factory) {
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";
  var core = breeze.core;

  var MetadataStore = breeze.MetadataStore;
  var JsonResultsAdapter = breeze.JsonResultsAdapter;
  var DataProperty = breeze.DataProperty;
  var DataType = breeze.DataType;
  var AutoGeneratedKeyType = breeze.AutoGeneratedKeyType;

  var OData;

  var ctor = function DataServiceODataAdapter() {
    this.name = "OData";
  };

  var proto = ctor.prototype; // minifies better (as seen in jQuery)

  proto.initialize = function () {
    OData = core.requireLib("OData", "Needed to support remote OData services");
    OData.jsonHandler.recognizeDates = true;
  };
  // borrow from AbstractDataServiceAdapter
  var abstractDsaProto = breeze.AbstractDataServiceAdapter.prototype;
  proto._catchNoConnectionError = abstractDsaProto._catchNoConnectionError;
  proto.changeRequestInterceptor = abstractDsaProto.changeRequestInterceptor;
  proto._createChangeRequestInterceptor = abstractDsaProto._createChangeRequestInterceptor;
  proto.headers = { "DataServiceVersion": "2.0" };

  // Absolute URL is the default as of Breeze 1.5.5.  
  // To use relative URL (like pre-1.5.5), add adapterInstance.relativeUrl = true:
  //
  //     var ds = breeze.config.initializeAdapterInstance("dataService", "webApiOData");
  //     ds.relativeUrl = true; 
  //
  // To use custom url construction, add adapterInstance.relativeUrl = myfunction(dataService, url):
  //
  //     var ds = breeze.config.initializeAdapterInstance("dataService", "webApiOData");
  //     ds.relativeUrl = function(dataService, url) {
  //        return somehowConvert(url);
  //     }
  //

  proto.getAbsoluteUrl = function (dataService, url){
    var serviceName = dataService.qualifyUrl('');
    // only prefix with serviceName if not already on the url
    var base = (core.stringStartsWith(url, serviceName)) ? '' : serviceName;
    // If no protocol, turn base into an absolute URI
    if (window && serviceName.indexOf('//') < 0) { 
      // no protocol; make it absolute
      base = window.location.protocol + '//' + window.location.host + 
            (core.stringStartsWith(serviceName, '/') ? '' : '/') +
            base;
    }
    return base + url;
  };

  proto.getRoutePrefix = function (dataService) {
      // Get the routePrefix from a Web API OData service name.
      // The routePrefix is presumed to be the pathname within the dataService.serviceName
      // Examples of servicename -> routePrefix:
      //   'http://localhost:55802/odata/' -> 'odata/'
      //   'http://198.154.121.75/service/odata/' -> 'service/odata/'
      var parser;
      if (typeof document === 'object') { // browser
          parser = document.createElement('a');
          parser.href = dataService.serviceName;
      } else { // node
          parser = url.parse(dataService.serviceName);
      }
      var prefix = parser.pathname;
      if (prefix[0] === '/') {
          prefix = prefix.substr(1);
      } // drop leading '/'  (all but IE)
      if (prefix.substr(-1) !== '/') {
          prefix += '/';
      }      // ensure trailing '/'
      return prefix;
  };

  // crude serializer.  Doesn't recurse
  function toQueryString(obj) {
    var parts = [];
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
      }
    }
    return parts.join("&");
  }

  proto.executeQuery = function (mappingContext) {

    var deferred = breeze.Q.defer();
    var url;
    if (this.relativeUrl === true) {
      url = mappingContext.getUrl();
    } else if (core.isFunction(this.relativeUrl)) {
      url = this.relativeUrl(mappingContext.dataService, mappingContext.getUrl());
    } else {
      url = this.getAbsoluteUrl(mappingContext.dataService, mappingContext.getUrl());
    }

    // Add query params if .withParameters was used
    if (!core.isEmpty(mappingContext.query.parameters)) {
      var paramString = toQueryString(mappingContext.query.parameters);
      var sep = url.indexOf("?") < 0 ? "?" : "&";
      url = url + sep + paramString;
    }

    OData.read({
          requestUri: url,
          headers: core.extend({}, this.headers)
        },
        function (data, response) {
          var inlineCount;
          if (data.__count) {
            // OData can return data.__count as a string
            inlineCount = parseInt(data.__count, 10);
          }
          // Odata returns different result structure when it returns multiple entities (data.results) vs single entity (data directly).
          // @see http://www.odata.org/documentation/odata-version-2-0/json-format/#RepresentingCollectionsOfEntries
          // and http://www.odata.org/documentation/odata-version-2-0/json-format/#RepresentingEntries
          var results;
          if (data.results) {
            results = data.results;
          } else {
            results = data;
          }
          return deferred.resolve({ results: results, inlineCount: inlineCount, httpResponse: response });
        },
        function (error) {
          return deferred.reject(createError(error, url));
        }
    );
    return deferred.promise;
  };


  proto.fetchMetadata = function (metadataStore, dataService) {

    var deferred = breeze.Q.defer();

    var serviceName = dataService.serviceName;

    var url;
    if (this.relativeUrl === true) {
      url = dataService.qualifyUrl('$metadata');
    } else if (core.isFunction(this.relativeUrl)) {
      url = this.relativeUrl(dataService, '$metadata');
    } else {
      url = this.getAbsoluteUrl(dataService, '$metadata');
    }

    var mheaders = core.extend({}, this.headers);
    mheaders.Accept = 'application/*; odata.metadata=full';

    // OData.read(url,
    OData.read({
          requestUri: url,
          // headers: { "Accept": "application/json"}
          headers: mheaders
        },
        function (data) {
          // data.dataServices.schema is an array of schemas. with properties of
          // entityContainer[], association[], entityType[], and namespace.
          if (!data || !data.dataServices) {
            var error = new Error("Metadata query failed for: " + url);
            return deferred.reject(error);
          }
          var csdlMetadata = data.dataServices;

          // might have been fetched by another query
          if (!metadataStore.hasMetadataFor(serviceName)) {
            try {
              metadataStore.importMetadata(csdlMetadata);
            } catch (e) {
              return deferred.reject(new Error("Metadata query failed for " + url + "; Unable to process returned metadata: " + e.message));
            }

            metadataStore.addDataService(dataService);
          }

          return deferred.resolve(csdlMetadata);

        }, function (error) {
          var err = createError(error, url);
          err.message = "Metadata query failed for: " + url + "; " + (err.message || "");
          return deferred.reject(err);
        },
        OData.metadataHandler
    );

    return deferred.promise;

  };



  proto.saveChanges = function (saveContext, saveBundle) {
    var adapter = saveContext.adapter = this;
    var deferred = breeze.Q.defer();

    var url;
    if (this.relativeUrl === true) {
      saveContext.routePrefix = adapter.getRoutePrefix(saveContext.dataService);
      url = saveContext.dataService.qualifyUrl("$batch");
    } else if (core.isFunction(adapter.relativeUrl)) {
      saveContext.routePrefix = adapter.relativeUrl(saveContext.dataService, '');
      url = saveContext.routePrefix + '$batch';
    } else {
      saveContext.routePrefix = adapter.getAbsoluteUrl(saveContext.dataService, '');
      url = saveContext.routePrefix + '$batch';
    }

    var requestData = createChangeRequests(saveContext, saveBundle);
    var tempKeys = saveContext.tempKeys;
    var contentKeys = saveContext.contentKeys;

    OData.request({
      headers: core.extend({}, this.headers),
      requestUri: url,
      method: "POST",
      data: requestData
    }, function (data, response) {
      var entities = [];
      var keyMappings = [];
      var saveResult = { entities: entities, keyMappings: keyMappings };
      data.__batchResponses.forEach(function (br) {
        br.__changeResponses.forEach(function (cr) {
          var response = cr.response || cr;
          var statusCode = response.statusCode;
          if ((!statusCode) || statusCode >= 400) {
            deferred.reject(createError(cr, url));
            return;
          }

          var contentId = cr.headers["Content-ID"];
          // Olingo sends different case of 'ID' for the header name.
          if (!contentId) {
            contentId = cr.headers["Content-Id"];
          }

          var rawEntity = cr.data;
          if (rawEntity) {
            var tempKey = tempKeys[contentId];
            if (tempKey) {
              var entityType = tempKey.entityType;
              if (entityType.autoGeneratedKeyType !== AutoGeneratedKeyType.None) {
                var tempValue = tempKey.values[0];
                var realKey = entityType.getEntityKeyFromRawEntity(rawEntity, DataProperty.getRawValueFromServer);
                var keyMapping = { entityTypeName: entityType.name, tempValue: tempValue, realValue: realKey.values[0] };
                keyMappings.push(keyMapping);
              }
            }
            entities.push(rawEntity);
          } else {
            var origEntity = contentKeys[contentId];
            entities.push(origEntity);
          }
        });
      });
      return deferred.resolve(saveResult);
    }, function (err) {
      return deferred.reject(createError(err, url));
    }, OData.batchHandler);

    return deferred.promise;

  };

  proto.jsonResultsAdapter = new JsonResultsAdapter({
    name: "OData_default",

    visitNode: function (node, mappingContext, nodeContext) {
      var result = {};
      if (node == null) return result;
      var metadata = node.__metadata;
      if (metadata != null) {
        // TODO: may be able to make this more efficient by caching of the previous value.
        var entityTypeName = MetadataStore.normalizeTypeName(metadata.type);
        var et = entityTypeName && mappingContext.entityManager.metadataStore.getEntityType(entityTypeName, true);
        // OData response doesn't distinguish a projection from a whole entity.
        // We'll assume that whole-entity data would have at least as many properties  (<=)
        // as the EntityType has mapped properties on the basis that
        // most projections remove properties rather than add them.
        // If not, assume it's a projection and do NOT treat as an entity
        if (et && et._mappedPropertiesCount <= Object.keys(node).length - 1) {
          // if (et && et._mappedPropertiesCount === Object.keys(node).length - 1) { // OLD
          result.entityType = et;
          var uriKey = metadata.uri || metadata.id;
          if (uriKey) {
            // Strip baseUri to make uriKey a relative uri
            // Todo: why is this necessary when absolute works for every OData source tested?
            var re = new RegExp('^' + mappingContext.dataService.serviceName, 'i');
            uriKey = uriKey.replace(re, '');
          }
          result.extraMetadata = {
            uriKey: uriKey,
            etag: metadata.etag
          }
        }
      }
      // OData v3 - projection arrays will be enclosed in a results array
      if (node.results) {
        result.node = node.results;
      }

      var propertyName = nodeContext.propertyName;
      result.ignore = node.__deferred != null || propertyName === "__metadata" ||
        // EntityKey properties can be produced by EDMX models
          (propertyName === "EntityKey" && node.$type && core.stringStartsWith(node.$type, "System.Data"));
      return result;
    }

  });

  function transformValue(prop, val) {
    if (prop.isUnmapped) return undefined;
    if (prop.dataType === DataType.DateTimeOffset) {
      // The datajs lib tries to treat client dateTimes that are defined as DateTimeOffset on the server differently
      // from other dateTimes. This fix compensates before the save.
      val = val && new Date(val.getTime() - (val.getTimezoneOffset() * 60000));
    } else if (prop.dataType.quoteJsonOData) {
      val = val != null ? val.toString() : val;
    }
    return val;
  }

  function createChangeRequests(saveContext, saveBundle) {
    var changeRequestInterceptor = saveContext.adapter._createChangeRequestInterceptor(saveContext, saveBundle);
    var changeRequests = [];
    var tempKeys = [];
    var contentKeys = [];
    var entityManager = saveContext.entityManager;
    var helper = entityManager.helper;
    var id = 0;
    var routePrefix = saveContext.routePrefix;

    saveBundle.entities.forEach(function (entity, index) {
      var aspect = entity.entityAspect;
      id = id + 1; // we are deliberately skipping id=0 because Content-ID = 0 seems to be ignored.
      var request = { headers: { "Content-ID": id, "DataServiceVersion": "2.0" } };
      contentKeys[id] = entity;
      if (aspect.entityState.isAdded()) {
        request.requestUri = routePrefix + entity.entityType.defaultResourceName;
        request.method = "POST";
        request.data = helper.unwrapInstance(entity, transformValue);
        tempKeys[id] = aspect.getKey();
      } else if (aspect.entityState.isModified()) {
        updateDeleteMergeRequest(request, aspect, routePrefix);
        request.method = "MERGE";
        request.data = helper.unwrapChangedValues(entity, entityManager.metadataStore, transformValue);
        // should be a PATCH/MERGE
      } else if (aspect.entityState.isDeleted()) {
        updateDeleteMergeRequest(request, aspect, routePrefix);
        request.method = "DELETE";
      } else {
        return;
      }
      request = changeRequestInterceptor.getRequest(request, entity, index);
      changeRequests.push(request);
    });
    saveContext.contentKeys = contentKeys;
    saveContext.tempKeys = tempKeys;
    changeRequestInterceptor.done(changeRequests);
    return {
      __batchRequests: [
        {
          __changeRequests: changeRequests
        }
      ]
    };

  }

  function updateDeleteMergeRequest(request, aspect, routePrefix) {
    var uriKey;
    var extraMetadata = aspect.extraMetadata;
    if (extraMetadata == null) {
      uriKey = getUriKey(aspect);
      aspect.extraMetadata = {
        uriKey: uriKey
      }
    } else {
      uriKey = extraMetadata.uriKey;
      if (extraMetadata.etag) {
        request.headers["If-Match"] = extraMetadata.etag;
      }
    }
    request.requestUri =
      // use routePrefix if uriKey lacks protocol (i.e., relative uri)
      uriKey.indexOf('//') > 0 ? uriKey : routePrefix + uriKey;
  }

  function getUriKey(aspect) {
    var entityType = aspect.entity.entityType;
    var resourceName = entityType.defaultResourceName;
    var kps = entityType.keyProperties;
    var uriKey = resourceName + "(";
    if (kps.length === 1) {
      uriKey = uriKey + fmtProperty(kps[0], aspect) + ")";
    } else {
      var delim = "";
      kps.forEach(function (kp) {
        uriKey = uriKey + delim + kp.nameOnServer + "=" + fmtProperty(kp, aspect);
        delim = ",";
      });
      uriKey = uriKey + ")";
    }
    return uriKey;
  }

  function fmtProperty(prop, aspect) {
    return prop.dataType.fmtOData(aspect.getPropertyValue(prop.name));
  }

  function createError(error, url) {
    // OData errors can have the message buried very deeply - and nonobviously
    // this code is tricky so be careful changing the response.body parsing.
    var result = new Error();
    var response = error && error.response;
    if (!response) {
      // in case DataJS returns "No handler for this data"
      result.message = error;
      result.statusText = error;
      return result;
    }
    result.message = response.statusText;
    result.statusText = response.statusText;
    result.status = response.statusCode;
    // non std
    if (url) result.url = url;
    result.body = response.body;
    if (response.body) {
      var nextErr;
      try {
        var body = JSON.parse(response.body);
        result.body = body;
        // OData v3 logic
        if (body['odata.error']) {
          body = body['odata.error'];
        }
        var msg = "";
        do {
          nextErr = body.error || body.innererror;
          if (!nextErr) msg = msg + getMessage(body);
          nextErr = nextErr || body.internalexception;
          body = nextErr || body;
        } while (nextErr);
        if (msg.length > 0) {
          result.message = msg;
        }
      } catch (e) {

      }
    }
    proto._catchNoConnectionError(result);
    return result;
  }

  function getMessage(body) {
    var msg = body.message || "";
    return ((typeof (msg) === "string") ? msg : msg.value) + "; ";
  }

  breeze.config.registerAdapter("dataService", ctor);


  var webApiODataCtor = function () {
    this.name = "webApiOData";
  }

  breeze.core.extend(webApiODataCtor.prototype, proto);

  breeze.config.registerAdapter("dataService", webApiODataCtor);
  // OData 4 adapter
  var webApiOData4Ctor = function () {
    this.name = "webApiOData4";
  }
  breeze.core.extend(webApiOData4Ctor.prototype, webApiODataCtor.prototype);
  webApiOData4Ctor.prototype.initialize = function () {
    // Aargh... they moved the cheese.
    var datajs = core.requireLib("datajs", "Needed to support remote OData v4 services");
    OData = datajs.V4.oData;
    OData.json.jsonHandler.recognizeDates = true;
  };
  webApiOData4Ctor.prototype.headers = { "OData-Version": "4.0" };
  breeze.config.registerAdapter("dataService", webApiOData4Ctor);


}));;(function (factory) {
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";

  var MetadataStore = breeze.MetadataStore;
  var JsonResultsAdapter = breeze.JsonResultsAdapter;
  var AbstractDataServiceAdapter = breeze.AbstractDataServiceAdapter;

  var ctor = function DataServiceWebApiAdapter() {
    this.name = "webApi";
  };
  var proto = ctor.prototype = new AbstractDataServiceAdapter();

  proto._prepareSaveBundle = function (saveContext, saveBundle) {
    var changeRequestInterceptor = this._createChangeRequestInterceptor(saveContext, saveBundle);
    var em = saveContext.entityManager;
    var metadataStore = em.metadataStore;
    var helper = em.helper;

    saveBundle.entities = saveBundle.entities.map(function (e, ix) {
      var rawEntity = helper.unwrapInstance(e);

      var autoGeneratedKey = null;
      if (e.entityType.autoGeneratedKeyType !== breeze.AutoGeneratedKeyType.None) {
        autoGeneratedKey = {
          propertyName: e.entityType.keyProperties[0].nameOnServer,
          autoGeneratedKeyType: e.entityType.autoGeneratedKeyType.name
        };
      }

      var originalValuesOnServer = helper.unwrapOriginalValues(e, metadataStore);
      rawEntity.entityAspect = {
        entityTypeName: e.entityType.name,
        defaultResourceName: e.entityType.defaultResourceName,
        entityState: e.entityAspect.entityState.name,
        originalValuesMap: originalValuesOnServer,
        autoGeneratedKey: autoGeneratedKey
      };
      rawEntity = changeRequestInterceptor.getRequest(rawEntity, e, ix);
      return rawEntity;
    });

    saveBundle.saveOptions = { tag: saveBundle.saveOptions.tag };
    changeRequestInterceptor.done(saveBundle.entities);
    return saveBundle;
  };

  proto._prepareSaveResult = function (saveContext, data) {
    // use the jsonResultAdapter to extractResults and extractKeyMappings
    var jra = saveContext.dataService.jsonResultsAdapter || this.jsonResultsAdapter;
    var entities = jra.extractSaveResults(data) || [];
    var keyMappings = jra.extractKeyMappings(data) || [];
    var deletedKeys = jra.extractDeletedKeys ? (jra.extractDeletedKeys(data)) || [] : [];

    if (keyMappings.length) {
      // HACK: need to change the 'case' of properties in the saveResult
      // but KeyMapping properties internally are still ucase. ugh...
      keyMappings = keyMappings.map(function (km) {
        if (km.entityTypeName) return km; // it's already lower case

        var entityTypeName = MetadataStore.normalizeTypeName(km.EntityTypeName);
        return { entityTypeName: entityTypeName, tempValue: km.TempValue, realValue: km.RealValue };
      });
    }

    if (deletedKeys.length) {
      deletedKeys = deletedKeys.map(function(dk) {
        if (dk.entityTypeName) return dk; // it's already lower case
        var entityTypeName = MetadataStore.normalizeTypeName(dk.EntityTypeName);
        // NOTE the dk.KeyValue => keyValues transition - needed because we are deserializing an .NET EntityKey
        return { entityTypeName: entityTypeName, keyValues: dk.KeyValue }; 
      });
    }
    
    return { entities: entities, keyMappings: keyMappings, deletedKeys: deletedKeys };
  };

  proto.jsonResultsAdapter = new JsonResultsAdapter({

    name: "webApi_default",

    visitNode: function (node, mappingContext, nodeContext) {
      if (node == null) return {};
      var entityTypeName = MetadataStore.normalizeTypeName(node.$type);
      var entityType = entityTypeName && mappingContext.entityManager.metadataStore._getEntityType(entityTypeName, true);
      var propertyName = nodeContext.propertyName;
      var ignore = propertyName && propertyName.substr(0, 1) === "$";

      return {
        entityType: entityType,
        nodeId: node.$id,
        nodeRefId: node.$ref,
        ignore: ignore
      };
    }

  });


  breeze.config.registerAdapter("dataService", ctor);

}));;(function (factory) {
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";
  var core = breeze.core;

  var ctor = function ModelLibraryBackingStoreAdapter() {
    this.name = "backingStore";
  };
  // protoFn used instead of proto here to avoid naming collision with function params.
  var protoFn = ctor.prototype;

  protoFn.initialize = function () {

  };

  protoFn.getTrackablePropertyNames = function (entity) {
    var names = [];
    for (var p in entity) {
      if (p === "entityType") continue;
      if (p === "_$typeName") continue;
      if (p === "_pendingSets") continue;
      if (p === "_backingStore") continue;
      var val = entity[p];
      if (!core.isFunction(val)) {
        names.push(p);
      }
    }
    return names;
  };

  // This method is called during Metadata initialization
  protoFn.initializeEntityPrototype = function (proto) {

    proto.getProperty = function (propertyName) {
      return this[propertyName];
    };

    proto.setProperty = function (propertyName, value) {
      //if (!this._backingStore.hasOwnProperty(propertyName)) {
      //    throw new Error("Unknown property name:" + propertyName);
      //}
      this[propertyName] = value;
      // allow setProperty chaining.
      return this;
    };

    movePropDefsToProto(proto);
  };

  // This method is called when an EntityAspect is first created - this will occur as part of the entityType.createEntity call.
  // which can be called either directly or via standard query materialization

  // entity is either an entity or a complexObject
  protoFn.startTracking = function (entity, proto) {
    // can't touch the normal property sets within this method - access the backingStore directly instead.
    var bs = movePropsToBackingStore(entity);

    // assign default values to the entity
    var stype = entity.entityType || entity.complexType;
    stype.getProperties().forEach(function (prop) {

      var propName = prop.name;
      var val = entity[propName];

      if (prop.isDataProperty) {
        if (prop.isComplexProperty) {
          if (prop.isScalar) {
            val = prop.dataType._createInstanceCore(entity, prop);
          } else {
            val = breeze.makeComplexArray([], entity, prop);
          }
        } else if (!prop.isScalar) {
          val = breeze.makePrimitiveArray([], entity, prop);
        } else if (val === undefined) {
          val = prop.defaultValue;
        }

      } else if (prop.isNavigationProperty) {
        if (val !== undefined) {
          throw new Error("Cannot assign a navigation property in an entity ctor.: " + propName);
        }
        if (prop.isScalar) {
          // TODO: change this to nullstob later.
          val = null;
        } else {
          val = breeze.makeRelationArray([], entity, prop);
        }
      } else {
        throw new Error("unknown property: " + propName);
      }
      // can't touch the normal property sets within this method (IE9 Bug) - so we access the backingStore directly instead.
      // otherwise we could just do
      // entity[propName] = val
      // after all of the interception logic had been injected.
      if (prop.isSettable || prop.isNavigationProperty) {
        bs[propName] = val;
      }
    });
  };


  // private methods

  // This method is called during Metadata initialization to correctly "wrap" properties.
  function movePropDefsToProto(proto) {
    var stype = proto.entityType || proto.complexType;
    var extra = stype._extra;

    var alreadyWrapped = extra.alreadyWrappedProps || {};

    stype.getProperties().forEach(function (prop) {
      var propName = prop.name;
      // we only want to wrap props that haven't already been wrapped
      if (alreadyWrapped[propName]) return;

      // If property is already defined on the prototype then wrap it in another propertyDescriptor.
      // otherwise create a propDescriptor for it.
      var descr;
      if (propName in proto) {
        descr = wrapPropDescription(proto, prop);
      } else {
        descr = makePropDescription(proto, prop);
      }
      // descr will be null for a wrapped descr that is not configurable
      if (descr != null) {
        Object.defineProperty(proto, propName, descr);
      }
      alreadyWrapped[propName] = true;
    });
    extra.alreadyWrappedProps = alreadyWrapped;
  }

  // This method is called when an instance is first created via materialization or createEntity.
  // this method cannot be called while a 'defineProperty' accessor is executing
  // because of IE bug mentioned above.

  function movePropsToBackingStore(instance) {

    var bs = getBackingStore(instance);
    var proto = Object.getPrototypeOf(instance);
    var stype = proto.entityType || proto.complexType;
    stype.getProperties().forEach(function (prop) {
      var propName = prop.name;
      if (prop.isUnmapped) {
        // insure that any unmapped properties that were added after entityType
        // was first created are wrapped with a property descriptor.
        if (!core.getPropertyDescriptor(proto, propName)) {
          var descr = makePropDescription(proto, prop);
          Object.defineProperty(proto, propName, descr);
        }
      }
      if (!instance.hasOwnProperty(propName)) return;
      // pulls off the value, removes the instance property and then rewrites it via ES5 accessor
      var value = instance[propName];
      delete instance[propName];
      instance[propName] = value;
    });
    return bs;
  }



  function makePropDescription(proto, property) {
    var propName = property.name;
    var pendingStores = proto._pendingBackingStores;
    if (!pendingStores) {
      pendingStores = [];
      proto._pendingBackingStores = pendingStores;
    }
    var descr = {
      get: function () {
        var bs = this._backingStore || getBackingStore(this);
        return bs[propName];
      },
      set: function (value) {
        // IE9 cannot touch instance._backingStore here
        var bs = this._backingStore || getPendingBackingStore(this);
        var accessorFn = getAccessorFn(bs, propName);
        this._$interceptor(property, value, accessorFn);
      },
      enumerable: true,
      configurable: true
    };

    descr.set.rawSet = function (value) {
      var bs = this._backingStore || getPendingBackingStore(this);
      var accessorFn = getAccessorFn(bs, propName);
      accessorFn(value);
    };
    return descr;

  }

  function getAccessorFn(bs, propName) {
    return function() {
      if (arguments.length === 0) {
        return bs[propName];
      } else {
        bs[propName] = arguments[0];
        return undefined;
      }
    };
  }


  function wrapPropDescription(proto, property) {
    if (!proto.hasOwnProperty(property.name)) {
      var nextProto = Object.getPrototypeOf(proto);
      return wrapPropDescription(nextProto, property);
    }

    var propDescr = Object.getOwnPropertyDescriptor(proto, property.name);
    // if not configurable; we can't touch it - so leave.
    if (!propDescr.configurable) return undefined;
    // if a data descriptor - don't change it - this is basically a static property - i.e. defined on every instance of the type with the same value.
    if (propDescr.value) return undefined;
    // if a read only property descriptor - no need to change it.
    if (!propDescr.set) return undefined;

    var localAccessorFn = function (entity) {
      return function () {
        if (arguments.length === 0) {
          return propDescr.get.bind(entity)();
        } else {
          var set = propDescr.set;
          while (set.rawSet) {
              set = set.rawSet;
          }
          set.bind(entity)(arguments[0]);
          return undefined;
        }
      };
    };

    var newDescr = {
      get: function () {
        return propDescr.get.bind(this)();
      },
      set: function (value) {
        this._$interceptor(property, value, localAccessorFn(this));
      },
      enumerable: propDescr.enumerable,
      configurable: true
    };
    newDescr.set.rawSet = propDescr.set;
    return newDescr;
  };


  function getBackingStore(instance) {
    var proto = Object.getPrototypeOf(instance);
    processPendingStores(proto);
    var bs = instance._backingStore;
    if (!bs) {
      bs = {};
      instance._backingStore = bs;
    }
    return bs;
  }

  // workaround for IE9 bug where instance properties cannot be changed when executing a property 'set' method.
  function getPendingBackingStore(instance) {
    var proto = Object.getPrototypeOf(instance);
    var pendingStores = proto._pendingBackingStores;
    var pending = core.arrayFirst(pendingStores, function (pending) {
      return pending.entity === instance;
    });
    if (pending) return pending.backingStore;
    var bs = {};
    pendingStores.push({ entity: instance, backingStore: bs });
    return bs;
  }

  function processPendingStores(proto) {
    var pendingStores = proto._pendingBackingStores;
    if (pendingStores) {
      pendingStores.forEach(function (pending) {
        pending.entity._backingStore = pending.backingStore;
      });
      pendingStores.length = 0;
    }
  }


  breeze.config.registerAdapter("modelLibrary", ctor);

}));
;(function (factory) {
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";
  var core = breeze.core;
  var canIsolateES5Props = core.__isES5Supported;

  var ko;

  var ctor = function ModelLibraryKnockoutAdapter() {
    this.name = "ko";
  };
  // protoFn used instead of proto here to avoid naming collision with function params.
  var protoFn = ctor.prototype;

  protoFn.initialize = function () {
    ko = core.requireLib("ko;knockout", "The Knockout library");
    ko.extenders.intercept = function (target, interceptorOptions) {
      var instance = interceptorOptions.instance;
      var property = interceptorOptions.property;

      // create a computed observable to intercept writes to our observable
      var result;
      if (target.splice) {
        result = ko.computed({
          read: target  //always return the original observables value
        });
      } else {
        result = ko.computed({
          read: target,  //always return the original observables value
          write: function (newValue) {
            instance._$interceptor(property, newValue, target);
            return instance;
          }
        });
      }
      //return the new computed observable
      return result;
    };

  };

  protoFn.getTrackablePropertyNames = function (entity) {
    var names = [];
    for (var p in entity) {
      if (p === "entityType") continue;
      if (p === "_$typeName") continue;

      var propDescr = getES5PropDescriptor(entity, p);
      if (propDescr && propDescr.get) {
        names.push(p);
      } else {
        var val = entity[p];
        if (ko.isObservable(val)) {
          names.push(p);
        } else if (!core.isFunction(val)) {
          names.push(p);
        }
      }
    }
    return names;
  };

  protoFn.initializeEntityPrototype = function (proto) {

    proto.getProperty = function (propertyName) {
      return this[propertyName]();
    };

    proto.setProperty = function (propertyName, value) {
      this[propertyName](value);
      // allow set property chaining.
      return this;
    };

    if (canIsolateES5Props) {
      isolateES5Props(proto);
    }

  };

  function isolateES5Props(proto) {

    var stype = proto.entityType || proto.complexType;
    var es5Descriptors = {};
    stype.getProperties().forEach(function (prop) {
      var propDescr = getES5PropDescriptor(proto, prop.name);
      if (propDescr) {
        es5Descriptors[prop.name] = propDescr;
      }
    });
    if (!core.isEmpty(es5Descriptors)) {
      var extra = stype._extra;
      extra.es5Descriptors = es5Descriptors;
      stype._koDummy = ko.observable(null);
    }

  }

  function getES5PropDescriptor(proto, propName) {
    if (!canIsolateES5Props) {
      return null;
    }
    if (proto.hasOwnProperty(propName)) {
      return Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(proto, propName);
    } else {
      var nextProto = Object.getPrototypeOf(proto);
      return nextProto ? getES5PropDescriptor(nextProto, propName) : null;
    }
  }

  protoFn.startTracking = function (entity, proto) {
    // create ko's for each property and assign defaultValues

    var stype = entity.entityType || entity.complexType;
    var es5Descriptors = stype._extra.es5Descriptors || {};

    // sort unmapped properties to the end
    stype.getProperties().sort(function (p1, p2) {
      var v1 = p1.isUnmapped ? 1 : 0;
      var v2 = p2.isUnmapped ? 1 : 0;
      return v1 - v2;
    }).forEach(function (prop) {
      var propName = prop.name;
      var val = entity[propName];
      var propDescr = es5Descriptors[propName];
      var koObj;

      // check if property is an ES5 property
      if (propDescr) {
        var getFn = propDescr.get.bind(entity);
        if (propDescr.set) {
          var setFn = propDescr.set.bind(entity);
          var rawAccessorFn = function (newValue) {
            if (arguments.length === 0) {
              getFn();
              return;
            } else {
              setFn(newValue);
            }
          }
          koObj = ko.computed({
            read: function () {
              stype._koDummy();
              return getFn();
            },
            write: function (newValue) {
              entity._$interceptor(prop, newValue, rawAccessorFn);
              stype._koDummy.valueHasMutated();
              return entity;
            }
          });
        } else {
          koObj = ko.computed({
            read: getFn,
            write: function () {
            }

          });
        }
        // check if property is already exposed as a ko object
      } else if (ko.isObservable(val)) {
        if (prop.isNavigationProperty) {
          throw new Error("Cannot assign a navigation property in an entity ctor.: " + propName);
        }
        koObj = val;
        // otherwise
      } else {
        val = initializeValueForProp(entity, prop, val);
        koObj = prop.isScalar ? ko.observable(val) : ko.observableArray(val);
      }


      if (prop.isScalar) {
        if (propDescr) {
          Object.defineProperty(entity, propName, {
            enumerable: true,
            configurable: true,
            writable: true,
            value: koObj
          });
        } else {
          var koExt = koObj.extend({ intercept: { instance: entity, property: prop } });
          entity[propName] = koExt;
        }
      } else {
        val._koObj = koObj;
        // code to suppress extra breeze notification when
        // ko's array methods are called.
        koObj.subscribe(onBeforeChange, null, "beforeChange");
        // code to insure that any direct breeze changes notify ko
        val.arrayChanged.subscribe(onArrayChanged);

        koObj.equalityComparer = function () {
          throw new Error("Collection navigation properties may NOT be set.");
        };
        entity[propName] = koObj;
      }

    });

  };

  function initializeValueForProp(entity, prop, val) {
    if (prop.isDataProperty) {
      if (prop.isComplexProperty) {
        // TODO: right now we create Empty complexObjects here - these should actually come from the entity
        if (prop.isScalar) {
          val = prop.dataType._createInstanceCore(entity, prop);
        } else {
          val = breeze.makeComplexArray([], entity, prop);
        }
      } else if (!prop.isScalar) {
        val = breeze.makePrimitiveArray([], entity, prop);
      } else if (val === undefined) {
        val = prop.defaultValue;
      }

    } else if (prop.isNavigationProperty) {
      if (val !== undefined) {
        throw new Error("Cannot assign a navigation property in an entity ctor.: " + prop.name);
      }
      if (prop.isScalar) {
        // TODO: change this to nullEntity later.
        val = null;
      } else {
        val = breeze.makeRelationArray([], entity, prop);
      }
    } else {
      throw new Error("unknown property: " + prop.name);
    }
    return val;
  }


  function onBeforeChange(args) {
    args._koObj._suppressBreeze = true;
  }

  function onArrayChanged(args) {
    var koObj = args.array._koObj;
    if (koObj._suppressBreeze) {
      koObj._suppressBreeze = false;
    } else {
      koObj.valueHasMutated();
    }
  }

  breeze.config.registerAdapter("modelLibrary", ctor);

}));
;(function (factory) {
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";

  var EntityType = breeze.EntityType;

  var ctor = function UriBuilderJsonAdapter() {
    this.name = "json";
  };
  var proto = ctor.prototype;

  proto.initialize = function() {};

  proto.buildUri = function (entityQuery, metadataStore) {
    // force entityType validation;
    var entityType = entityQuery._getFromEntityType(metadataStore, false);
    if (!entityType) entityType = new EntityType(metadataStore);
    var json = entityQuery.toJSONExt( { entityType: entityType, toNameOnServer: true});
    json.from = undefined;
    json.queryOptions = undefined;

    var jsonString = JSON.stringify(json);
    var urlBody = encodeURIComponent(jsonString);
    return entityQuery.resourceName + "?" + urlBody;

  };

  breeze.config.registerAdapter("uriBuilder", ctor);

}));



;(function (factory) {
  if (typeof breeze === "object") {
    factory(breeze);
  } else if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
    // CommonJS or Node: hard-coded dependency on "breeze-client"
    factory(require("breeze-client"));
  } else if (typeof define === "function" && define["amd"]) {
    // AMD anonymous module with hard-coded dependency on "breeze-client"
    define(["breeze-client"], factory);
  }
}(function (breeze) {
  "use strict";
  var EntityType = breeze.EntityType;
  var toODataFragmentVisitor;

  var ctor = function UriBuilderODataAdapter() {
    this.name = "odata";
  };
  var proto = ctor.prototype;

  proto.initialize = function() {};

  proto.buildUri = function (entityQuery, metadataStore) {
    // force entityType validation;
    var entityType = entityQuery._getFromEntityType(metadataStore, false);
    if (!entityType) {
      // anonymous type but still has naming convention info avail
      entityType = new EntityType(metadataStore);
    }

    var queryOptions = {};
    queryOptions["$filter"] = toWhereODataFragment(entityQuery.wherePredicate);
    queryOptions["$orderby"] = toOrderByODataFragment(entityQuery.orderByClause);

    if (entityQuery.skipCount) {
      queryOptions["$skip"] = entityQuery.skipCount;
    }

    if (entityQuery.takeCount != null) {
      queryOptions["$top"] = entityQuery.takeCount;
    }

    queryOptions["$expand"] = toExpandODataFragment(entityQuery.expandClause);
    queryOptions["$select"] = toSelectODataFragment(entityQuery.selectClause);

    if (entityQuery.inlineCountEnabled) {
      queryOptions["$inlinecount"] = "allpages";
    }

    var qoText = toQueryOptionsString(queryOptions);
    return entityQuery.resourceName + qoText;

    // private methods to this func.

    function toWhereODataFragment(wherePredicate) {
      if (!wherePredicate) return undefined;
      // validation occurs inside of the toODataFragment call here.
      return wherePredicate.visit({ entityType: entityType}, toODataFragmentVisitor );
    }

    function toOrderByODataFragment(orderByClause) {
      if (!orderByClause) return undefined;
      orderByClause.validate(entityType);
      var strings = orderByClause.items.map(function (item) {
        return entityType.clientPropertyPathToServer(item.propertyPath, "/") + (item.isDesc ? " desc" : "");
      });
      // should return something like CompanyName,Address/City desc
      return strings.join(',');
    }

    function toSelectODataFragment(selectClause) {
      if (!selectClause) return undefined;
      selectClause.validate(entityType);
      var frag = selectClause.propertyPaths.map(function (pp) {
        return  entityType.clientPropertyPathToServer(pp, "/");
      }).join(",");
      return frag;
    }

    function toExpandODataFragment(expandClause) {
      if (!expandClause) return undefined;
      // no validate on expand clauses currently.
      // expandClause.validate(entityType);
      var frag = expandClause.propertyPaths.map(function (pp) {
        return entityType.clientPropertyPathToServer(pp, "/");
      }).join(",");
      return frag;
    }

    function toQueryOptionsString(queryOptions) {
      var qoStrings = [];
      for (var qoName in queryOptions) {
        var qoValue = queryOptions[qoName];
        if (qoValue !== undefined) {
          if (qoValue instanceof Array) {
            qoValue.forEach(function (qov) {
              qoStrings.push(qoName + "=" + encodeURIComponent(qov));
            });
          } else {
            qoStrings.push(qoName + "=" + encodeURIComponent(qoValue));
          }
        }
      }

      if (qoStrings.length > 0) {
        return "?" + qoStrings.join("&");
      } else {
        return "";
      }
    }
  };

  breeze.Predicate.prototype.toODataFragment = function(context) {
    return this.visit( context, toODataFragmentVisitor);
  }

  toODataFragmentVisitor = (function () {
    var visitor = {

      passthruPredicate: function () {
        return this.value;
      },

      unaryPredicate: function (context) {
        var predVal = this.pred.visit(context);
        return odataOpFrom(this) + " " + "(" + predVal + ")";
      },

      binaryPredicate: function (context) {
        var expr1Val = this.expr1.visit(context);
        var expr2Val = this.expr2.visit(context);
        var prefix = context.prefix;
        if (prefix) {
          expr1Val = prefix + "/" + expr1Val;
        }

        var odataOp = odataOpFrom(this);

        if (this.op.key === 'in') {
          var result = expr2Val.map(function (v) {
            return "(" + expr1Val + " eq " + v + ")";
          }).join(" or ");
          return result;
        } else if (this.op.isFunction) {
          if (odataOp === "substringof") {
            return odataOp + "(" + expr2Val + "," + expr1Val + ") eq true";
          } else {
            return odataOp + "(" + expr1Val + "," + expr2Val + ") eq true";
          }
        } else {
          return expr1Val + " " + odataOp + " " + expr2Val;
        }
      },

      andOrPredicate: function (context) {
        var result = this.preds.map(function (pred) {
          var predVal = pred.visit(context);
          return "(" + predVal + ")";
        }).join(" " + odataOpFrom(this) + " ");
        return result;
      },

      anyAllPredicate: function (context) {
        var exprVal = this.expr.visit(context);
        if (!this.pred.op) {
          return exprVal + "/" + odataOpFrom(this) + "()";
        }
        var prefix = context.prefix;
        if (prefix) {
          exprVal = prefix + "/" + exprVal;
          prefix = "x" + (parseInt(prefix.substring(1)) + 1);
        } else {
          prefix = "x1";
        }
        // need to create a new context because of 'prefix'
        var newContext = breeze.core.extend({}, context);
        newContext.entityType = this.expr.dataType;
        newContext.prefix = prefix;
        var newPredVal = this.pred.visit(newContext);
        return exprVal + "/" + odataOpFrom(this) + "(" + prefix + ": " + newPredVal + ")";
      },

      litExpr: function () {
        if (Array.isArray(this.value)) {
          return this.value.map(function(v) { return this.dataType.fmtOData(v)}, this);
        } else {
          return this.dataType.fmtOData(this.value);
        }
      },

      propExpr: function (context) {
        var entityType = context.entityType;
        // '/' is the OData path delimiter
        return entityType ? entityType.clientPropertyPathToServer(this.propertyPath, "/") : this.propertyPath;
      },

      fnExpr: function (context) {
        var exprVals = this.exprs.map(function(expr) {
          return expr.visit(context);
        });
        return this.fnName + "(" + exprVals.join(",") + ")";
      }
    };

    var _operatorMap = {
      'contains': 'substringof'
    };

    function odataOpFrom(node) {
      var op = node.op.key;
      var odataOp = _operatorMap[op];
      return odataOp || op;
    }

    return visitor;
  }());

  breeze.config.registerAdapter("uriBuilder", ctor);

}));





;
// set defaults
// will no longer fail at initialization time if jQuery is not found.
breeze.config.initializeAdapterInstances( { dataService: "webApi", ajax: "jQuery", uriBuilder: "odata" });

var ko = __requireLibCore("ko");

if (ko) {
    breeze.config.initializeAdapterInstance("modelLibrary", "ko");
} else {
    breeze.config.initializeAdapterInstance("modelLibrary", "backingStore");
}

return breeze;
});
;
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";function e(){return Yt.apply(null,arguments)}function t(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function n(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function s(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}function i(e){return void 0===e}function r(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function a(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function o(e,t){var n,s=[];for(n=0;n<e.length;++n)s.push(t(e[n],n));return s}function u(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function l(e,t){for(var n in t)u(t,n)&&(e[n]=t[n]);return u(t,"toString")&&(e.toString=t.toString),u(t,"valueOf")&&(e.valueOf=t.valueOf),e}function d(e,t,n,s){return je(e,t,n,s,!0).utc()}function h(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}}function c(e){return null==e._pf&&(e._pf=h()),e._pf}function f(e){if(null==e._isValid){var t=c(e),n=Ot.call(t.parsedDateParts,function(e){return null!=e}),s=!isNaN(e._d.getTime())&&t.overflow<0&&!t.empty&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n);if(e._strict&&(s=s&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour),null!=Object.isFrozen&&Object.isFrozen(e))return s;e._isValid=s}return e._isValid}function m(e){var t=d(NaN);return null!=e?l(c(t),e):c(t).userInvalidated=!0,t}function _(e,t){var n,s,r;if(i(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),i(t._i)||(e._i=t._i),i(t._f)||(e._f=t._f),i(t._l)||(e._l=t._l),i(t._strict)||(e._strict=t._strict),i(t._tzm)||(e._tzm=t._tzm),i(t._isUTC)||(e._isUTC=t._isUTC),i(t._offset)||(e._offset=t._offset),i(t._pf)||(e._pf=c(t)),i(t._locale)||(e._locale=t._locale),xt.length>0)for(n=0;n<xt.length;n++)i(r=t[s=xt[n]])||(e[s]=r);return e}function y(t){_(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===Tt&&(Tt=!0,e.updateOffset(this),Tt=!1)}function g(e){return e instanceof y||null!=e&&null!=e._isAMomentObject}function p(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function w(e){var t=+e,n=0;return 0!==t&&isFinite(t)&&(n=p(t)),n}function v(e,t,n){var s,i=Math.min(e.length,t.length),r=Math.abs(e.length-t.length),a=0;for(s=0;s<i;s++)(n&&e[s]!==t[s]||!n&&w(e[s])!==w(t[s]))&&a++;return a+r}function M(t){!1===e.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function k(t,n){var s=!0;return l(function(){if(null!=e.deprecationHandler&&e.deprecationHandler(null,t),s){for(var i,r=[],a=0;a<arguments.length;a++){if(i="","object"==typeof arguments[a]){i+="\n["+a+"] ";for(var o in arguments[0])i+=o+": "+arguments[0][o]+", ";i=i.slice(0,-2)}else i=arguments[a];r.push(i)}M(t+"\nArguments: "+Array.prototype.slice.call(r).join("")+"\n"+(new Error).stack),s=!1}return n.apply(this,arguments)},n)}function S(t,n){null!=e.deprecationHandler&&e.deprecationHandler(t,n),bt[t]||(M(n),bt[t]=!0)}function D(e){return e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function Y(e,t){var s,i=l({},e);for(s in t)u(t,s)&&(n(e[s])&&n(t[s])?(i[s]={},l(i[s],e[s]),l(i[s],t[s])):null!=t[s]?i[s]=t[s]:delete i[s]);for(s in e)u(e,s)&&!u(t,s)&&n(e[s])&&(i[s]=l({},i[s]));return i}function O(e){null!=e&&this.set(e)}function x(e,t){var n=e.toLowerCase();Ut[n]=Ut[n+"s"]=Ut[t]=e}function T(e){return"string"==typeof e?Ut[e]||Ut[e.toLowerCase()]:void 0}function b(e){var t,n,s={};for(n in e)u(e,n)&&(t=T(n))&&(s[t]=e[n]);return s}function P(e,t){Nt[e]=t}function W(e){var t=[];for(var n in e)t.push({unit:n,priority:Nt[n]});return t.sort(function(e,t){return e.priority-t.priority}),t}function R(e,t,n){var s=""+Math.abs(e),i=t-s.length;return(e>=0?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+s}function C(e,t,n,s){var i=s;"string"==typeof s&&(i=function(){return this[s]()}),e&&(Vt[e]=i),t&&(Vt[t[0]]=function(){return R(i.apply(this,arguments),t[1],t[2])}),n&&(Vt[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function F(e){return e.match(/\[[\s\S]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"")}function U(e){var t,n,s=e.match(Ht);for(t=0,n=s.length;t<n;t++)Vt[s[t]]?s[t]=Vt[s[t]]:s[t]=F(s[t]);return function(t){var i,r="";for(i=0;i<n;i++)r+=D(s[i])?s[i].call(t,e):s[i];return r}}function N(e,t){return e.isValid()?(t=H(t,e.localeData()),Gt[t]=Gt[t]||U(t),Gt[t](e)):e.localeData().invalidDate()}function H(e,t){var n=5;for(Lt.lastIndex=0;n>=0&&Lt.test(e);)e=e.replace(Lt,function(e){return t.longDateFormat(e)||e}),Lt.lastIndex=0,n-=1;return e}function L(e,t,n){rn[e]=D(t)?t:function(e,s){return e&&n?n:t}}function G(e,t){return u(rn,e)?rn[e](t._strict,t._locale):new RegExp(V(e))}function V(e){return j(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i}))}function j(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function I(e,t){var n,s=t;for("string"==typeof e&&(e=[e]),r(t)&&(s=function(e,n){n[t]=w(e)}),n=0;n<e.length;n++)an[e[n]]=s}function E(e,t){I(e,function(e,n,s,i){s._w=s._w||{},t(e,s._w,s,i)})}function A(e,t,n){null!=t&&u(an,e)&&an[e](t,n._a,n,e)}function z(e){return Z(e)?366:365}function Z(e){return e%4==0&&e%100!=0||e%400==0}function $(t,n){return function(s){return null!=s?(J(this,t,s),e.updateOffset(this,n),this):q(this,t)}}function q(e,t){return e.isValid()?e._d["get"+(e._isUTC?"UTC":"")+t]():NaN}function J(e,t,n){e.isValid()&&!isNaN(n)&&("FullYear"===t&&Z(e.year())?e._d["set"+(e._isUTC?"UTC":"")+t](n,e.month(),Q(n,e.month())):e._d["set"+(e._isUTC?"UTC":"")+t](n))}function B(e,t){return(e%t+t)%t}function Q(e,t){if(isNaN(e)||isNaN(t))return NaN;var n=B(t,12);return e+=(t-n)/12,1===n?Z(e)?29:28:31-n%7%2}function X(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],s=0;s<12;++s)r=d([2e3,s]),this._shortMonthsParse[s]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[s]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===t?-1!==(i=yn.call(this._shortMonthsParse,a))?i:null:-1!==(i=yn.call(this._longMonthsParse,a))?i:null:"MMM"===t?-1!==(i=yn.call(this._shortMonthsParse,a))?i:-1!==(i=yn.call(this._longMonthsParse,a))?i:null:-1!==(i=yn.call(this._longMonthsParse,a))?i:-1!==(i=yn.call(this._shortMonthsParse,a))?i:null}function K(e,t){var n;if(!e.isValid())return e;if("string"==typeof t)if(/^\d+$/.test(t))t=w(t);else if(t=e.localeData().monthsParse(t),!r(t))return e;return n=Math.min(e.date(),Q(e.year(),t)),e._d["set"+(e._isUTC?"UTC":"")+"Month"](t,n),e}function ee(t){return null!=t?(K(this,t),e.updateOffset(this,!0),this):q(this,"Month")}function te(){function e(e,t){return t.length-e.length}var t,n,s=[],i=[],r=[];for(t=0;t<12;t++)n=d([2e3,t]),s.push(this.monthsShort(n,"")),i.push(this.months(n,"")),r.push(this.months(n,"")),r.push(this.monthsShort(n,""));for(s.sort(e),i.sort(e),r.sort(e),t=0;t<12;t++)s[t]=j(s[t]),i[t]=j(i[t]);for(t=0;t<24;t++)r[t]=j(r[t]);this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+s.join("|")+")","i")}function ne(e,t,n,s,i,r,a){var o=new Date(e,t,n,s,i,r,a);return e<100&&e>=0&&isFinite(o.getFullYear())&&o.setFullYear(e),o}function se(e){var t=new Date(Date.UTC.apply(null,arguments));return e<100&&e>=0&&isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e),t}function ie(e,t,n){var s=7+t-n;return-((7+se(e,0,s).getUTCDay()-t)%7)+s-1}function re(e,t,n,s,i){var r,a,o=1+7*(t-1)+(7+n-s)%7+ie(e,s,i);return o<=0?a=z(r=e-1)+o:o>z(e)?(r=e+1,a=o-z(e)):(r=e,a=o),{year:r,dayOfYear:a}}function ae(e,t,n){var s,i,r=ie(e.year(),t,n),a=Math.floor((e.dayOfYear()-r-1)/7)+1;return a<1?s=a+oe(i=e.year()-1,t,n):a>oe(e.year(),t,n)?(s=a-oe(e.year(),t,n),i=e.year()+1):(i=e.year(),s=a),{week:s,year:i}}function oe(e,t,n){var s=ie(e,t,n),i=ie(e+1,t,n);return(z(e)-s+i)/7}function ue(e,t){return"string"!=typeof e?e:isNaN(e)?"number"==typeof(e=t.weekdaysParse(e))?e:null:parseInt(e,10)}function le(e,t){return"string"==typeof e?t.weekdaysParse(e)%7||7:isNaN(e)?null:e}function de(e,t,n){var s,i,r,a=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=d([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=yn.call(this._weekdaysParse,a))?i:null:"ddd"===t?-1!==(i=yn.call(this._shortWeekdaysParse,a))?i:null:-1!==(i=yn.call(this._minWeekdaysParse,a))?i:null:"dddd"===t?-1!==(i=yn.call(this._weekdaysParse,a))?i:-1!==(i=yn.call(this._shortWeekdaysParse,a))?i:-1!==(i=yn.call(this._minWeekdaysParse,a))?i:null:"ddd"===t?-1!==(i=yn.call(this._shortWeekdaysParse,a))?i:-1!==(i=yn.call(this._weekdaysParse,a))?i:-1!==(i=yn.call(this._minWeekdaysParse,a))?i:null:-1!==(i=yn.call(this._minWeekdaysParse,a))?i:-1!==(i=yn.call(this._weekdaysParse,a))?i:-1!==(i=yn.call(this._shortWeekdaysParse,a))?i:null}function he(){function e(e,t){return t.length-e.length}var t,n,s,i,r,a=[],o=[],u=[],l=[];for(t=0;t<7;t++)n=d([2e3,1]).day(t),s=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),r=this.weekdays(n,""),a.push(s),o.push(i),u.push(r),l.push(s),l.push(i),l.push(r);for(a.sort(e),o.sort(e),u.sort(e),l.sort(e),t=0;t<7;t++)o[t]=j(o[t]),u[t]=j(u[t]),l[t]=j(l[t]);this._weekdaysRegex=new RegExp("^("+l.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+a.join("|")+")","i")}function ce(){return this.hours()%12||12}function fe(e,t){C(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function me(e,t){return t._meridiemParse}function _e(e){return e?e.toLowerCase().replace("_","-"):e}function ye(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=_e(e[r]).split("-")).length,n=(n=_e(e[r+1]))?n.split("-"):null;t>0;){if(s=ge(i.slice(0,t).join("-")))return s;if(n&&n.length>=t&&v(i,n,!0)>=t-1)break;t--}r++}return null}function ge(e){var t=null;if(!Fn[e]&&"undefined"!=typeof module&&module&&module.exports)try{t=Pn._abbr,require("./locale/"+e),pe(t)}catch(e){}return Fn[e]}function pe(e,t){var n;return e&&(n=i(t)?ve(e):we(e,t))&&(Pn=n),Pn._abbr}function we(e,t){if(null!==t){var n=Cn;if(t.abbr=e,null!=Fn[e])S("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),n=Fn[e]._config;else if(null!=t.parentLocale){if(null==Fn[t.parentLocale])return Un[t.parentLocale]||(Un[t.parentLocale]=[]),Un[t.parentLocale].push({name:e,config:t}),null;n=Fn[t.parentLocale]._config}return Fn[e]=new O(Y(n,t)),Un[e]&&Un[e].forEach(function(e){we(e.name,e.config)}),pe(e),Fn[e]}return delete Fn[e],null}function ve(e){var n;if(e&&e._locale&&e._locale._abbr&&(e=e._locale._abbr),!e)return Pn;if(!t(e)){if(n=ge(e))return n;e=[e]}return ye(e)}function Me(e){var t,n=e._a;return n&&-2===c(e).overflow&&(t=n[un]<0||n[un]>11?un:n[ln]<1||n[ln]>Q(n[on],n[un])?ln:n[dn]<0||n[dn]>24||24===n[dn]&&(0!==n[hn]||0!==n[cn]||0!==n[fn])?dn:n[hn]<0||n[hn]>59?hn:n[cn]<0||n[cn]>59?cn:n[fn]<0||n[fn]>999?fn:-1,c(e)._overflowDayOfYear&&(t<on||t>ln)&&(t=ln),c(e)._overflowWeeks&&-1===t&&(t=mn),c(e)._overflowWeekday&&-1===t&&(t=_n),c(e).overflow=t),e}function ke(e,t,n){return null!=e?e:null!=t?t:n}function Se(t){var n=new Date(e.now());return t._useUTC?[n.getUTCFullYear(),n.getUTCMonth(),n.getUTCDate()]:[n.getFullYear(),n.getMonth(),n.getDate()]}function De(e){var t,n,s,i,r=[];if(!e._d){for(s=Se(e),e._w&&null==e._a[ln]&&null==e._a[un]&&Ye(e),null!=e._dayOfYear&&(i=ke(e._a[on],s[on]),(e._dayOfYear>z(i)||0===e._dayOfYear)&&(c(e)._overflowDayOfYear=!0),n=se(i,0,e._dayOfYear),e._a[un]=n.getUTCMonth(),e._a[ln]=n.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=r[t]=s[t];for(;t<7;t++)e._a[t]=r[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[dn]&&0===e._a[hn]&&0===e._a[cn]&&0===e._a[fn]&&(e._nextDay=!0,e._a[dn]=0),e._d=(e._useUTC?se:ne).apply(null,r),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[dn]=24),e._w&&void 0!==e._w.d&&e._w.d!==e._d.getDay()&&(c(e).weekdayMismatch=!0)}}function Ye(e){var t,n,s,i,r,a,o,u;if(null!=(t=e._w).GG||null!=t.W||null!=t.E)r=1,a=4,n=ke(t.GG,e._a[on],ae(Ie(),1,4).year),s=ke(t.W,1),((i=ke(t.E,1))<1||i>7)&&(u=!0);else{r=e._locale._week.dow,a=e._locale._week.doy;var l=ae(Ie(),r,a);n=ke(t.gg,e._a[on],l.year),s=ke(t.w,l.week),null!=t.d?((i=t.d)<0||i>6)&&(u=!0):null!=t.e?(i=t.e+r,(t.e<0||t.e>6)&&(u=!0)):i=r}s<1||s>oe(n,r,a)?c(e)._overflowWeeks=!0:null!=u?c(e)._overflowWeekday=!0:(o=re(n,s,i,r,a),e._a[on]=o.year,e._dayOfYear=o.dayOfYear)}function Oe(e){var t,n,s,i,r,a,o=e._i,u=Nn.exec(o)||Hn.exec(o);if(u){for(c(e).iso=!0,t=0,n=Gn.length;t<n;t++)if(Gn[t][1].exec(u[1])){i=Gn[t][0],s=!1!==Gn[t][2];break}if(null==i)return void(e._isValid=!1);if(u[3]){for(t=0,n=Vn.length;t<n;t++)if(Vn[t][1].exec(u[3])){r=(u[2]||" ")+Vn[t][0];break}if(null==r)return void(e._isValid=!1)}if(!s&&null!=r)return void(e._isValid=!1);if(u[4]){if(!Ln.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=i+(r||"")+(a||""),Fe(e)}else e._isValid=!1}function xe(e,t,n,s,i,r){var a=[Te(e),vn.indexOf(t),parseInt(n,10),parseInt(s,10),parseInt(i,10)];return r&&a.push(parseInt(r,10)),a}function Te(e){var t=parseInt(e,10);return t<=49?2e3+t:t<=999?1900+t:t}function be(e){return e.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").trim()}function Pe(e,t,n){return!e||Yn.indexOf(e)===new Date(t[0],t[1],t[2]).getDay()||(c(n).weekdayMismatch=!0,n._isValid=!1,!1)}function We(e,t,n){if(e)return En[e];if(t)return 0;var s=parseInt(n,10),i=s%100;return 60*((s-i)/100)+i}function Re(e){var t=In.exec(be(e._i));if(t){var n=xe(t[4],t[3],t[2],t[5],t[6],t[7]);if(!Pe(t[1],n,e))return;e._a=n,e._tzm=We(t[8],t[9],t[10]),e._d=se.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),c(e).rfc2822=!0}else e._isValid=!1}function Ce(t){var n=jn.exec(t._i);null===n?(Oe(t),!1===t._isValid&&(delete t._isValid,Re(t),!1===t._isValid&&(delete t._isValid,e.createFromInputFallback(t)))):t._d=new Date(+n[1])}function Fe(t){if(t._f!==e.ISO_8601)if(t._f!==e.RFC_2822){t._a=[],c(t).empty=!0;var n,s,i,r,a,o=""+t._i,u=o.length,l=0;for(i=H(t._f,t._locale).match(Ht)||[],n=0;n<i.length;n++)r=i[n],(s=(o.match(G(r,t))||[])[0])&&((a=o.substr(0,o.indexOf(s))).length>0&&c(t).unusedInput.push(a),o=o.slice(o.indexOf(s)+s.length),l+=s.length),Vt[r]?(s?c(t).empty=!1:c(t).unusedTokens.push(r),A(r,s,t)):t._strict&&!s&&c(t).unusedTokens.push(r);c(t).charsLeftOver=u-l,o.length>0&&c(t).unusedInput.push(o),t._a[dn]<=12&&!0===c(t).bigHour&&t._a[dn]>0&&(c(t).bigHour=void 0),c(t).parsedDateParts=t._a.slice(0),c(t).meridiem=t._meridiem,t._a[dn]=Ue(t._locale,t._a[dn],t._meridiem),De(t),Me(t)}else Re(t);else Oe(t)}function Ue(e,t,n){var s;return null==n?t:null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((s=e.isPM(n))&&t<12&&(t+=12),s||12!==t||(t=0),t):t}function Ne(e){var t,n,s,i,r;if(0===e._f.length)return c(e).invalidFormat=!0,void(e._d=new Date(NaN));for(i=0;i<e._f.length;i++)r=0,t=_({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],Fe(t),f(t)&&(r+=c(t).charsLeftOver,r+=10*c(t).unusedTokens.length,c(t).score=r,(null==s||r<s)&&(s=r,n=t));l(e,n||t)}function He(e){if(!e._d){var t=b(e._i);e._a=o([t.year,t.month,t.day||t.date,t.hour,t.minute,t.second,t.millisecond],function(e){return e&&parseInt(e,10)}),De(e)}}function Le(e){var t=new y(Me(Ge(e)));return t._nextDay&&(t.add(1,"d"),t._nextDay=void 0),t}function Ge(e){var n=e._i,s=e._f;return e._locale=e._locale||ve(e._l),null===n||void 0===s&&""===n?m({nullInput:!0}):("string"==typeof n&&(e._i=n=e._locale.preparse(n)),g(n)?new y(Me(n)):(a(n)?e._d=n:t(s)?Ne(e):s?Fe(e):Ve(e),f(e)||(e._d=null),e))}function Ve(s){var u=s._i;i(u)?s._d=new Date(e.now()):a(u)?s._d=new Date(u.valueOf()):"string"==typeof u?Ce(s):t(u)?(s._a=o(u.slice(0),function(e){return parseInt(e,10)}),De(s)):n(u)?He(s):r(u)?s._d=new Date(u):e.createFromInputFallback(s)}function je(e,i,r,a,o){var u={};return!0!==r&&!1!==r||(a=r,r=void 0),(n(e)&&s(e)||t(e)&&0===e.length)&&(e=void 0),u._isAMomentObject=!0,u._useUTC=u._isUTC=o,u._l=r,u._i=e,u._f=i,u._strict=a,Le(u)}function Ie(e,t,n,s){return je(e,t,n,s,!1)}function Ee(e,n){var s,i;if(1===n.length&&t(n[0])&&(n=n[0]),!n.length)return Ie();for(s=n[0],i=1;i<n.length;++i)n[i].isValid()&&!n[i][e](s)||(s=n[i]);return s}function Ae(e){for(var t in e)if(-1===yn.call(Zn,t)||null!=e[t]&&isNaN(e[t]))return!1;for(var n=!1,s=0;s<Zn.length;++s)if(e[Zn[s]]){if(n)return!1;parseFloat(e[Zn[s]])!==w(e[Zn[s]])&&(n=!0)}return!0}function ze(e){var t=b(e),n=t.year||0,s=t.quarter||0,i=t.month||0,r=t.week||0,a=t.day||0,o=t.hour||0,u=t.minute||0,l=t.second||0,d=t.millisecond||0;this._isValid=Ae(t),this._milliseconds=+d+1e3*l+6e4*u+1e3*o*60*60,this._days=+a+7*r,this._months=+i+3*s+12*n,this._data={},this._locale=ve(),this._bubble()}function Ze(e){return e instanceof ze}function $e(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function qe(e,t){C(e,0,0,function(){var e=this.utcOffset(),n="+";return e<0&&(e=-e,n="-"),n+R(~~(e/60),2)+t+R(~~e%60,2)})}function Je(e,t){var n=(t||"").match(e);if(null===n)return null;var s=((n[n.length-1]||[])+"").match($n)||["-",0,0],i=60*s[1]+w(s[2]);return 0===i?0:"+"===s[0]?i:-i}function Be(t,n){var s,i;return n._isUTC?(s=n.clone(),i=(g(t)||a(t)?t.valueOf():Ie(t).valueOf())-s.valueOf(),s._d.setTime(s._d.valueOf()+i),e.updateOffset(s,!1),s):Ie(t).local()}function Qe(e){return 15*-Math.round(e._d.getTimezoneOffset()/15)}function Xe(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}function Ke(e,t){var n,s,i,a=e,o=null;return Ze(e)?a={ms:e._milliseconds,d:e._days,M:e._months}:r(e)?(a={},t?a[t]=e:a.milliseconds=e):(o=qn.exec(e))?(n="-"===o[1]?-1:1,a={y:0,d:w(o[ln])*n,h:w(o[dn])*n,m:w(o[hn])*n,s:w(o[cn])*n,ms:w($e(1e3*o[fn]))*n}):(o=Jn.exec(e))?(n="-"===o[1]?-1:(o[1],1),a={y:et(o[2],n),M:et(o[3],n),w:et(o[4],n),d:et(o[5],n),h:et(o[6],n),m:et(o[7],n),s:et(o[8],n)}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(i=nt(Ie(a.from),Ie(a.to)),(a={}).ms=i.milliseconds,a.M=i.months),s=new ze(a),Ze(e)&&u(e,"_locale")&&(s._locale=e._locale),s}function et(e,t){var n=e&&parseFloat(e.replace(",","."));return(isNaN(n)?0:n)*t}function tt(e,t){var n={milliseconds:0,months:0};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function nt(e,t){var n;return e.isValid()&&t.isValid()?(t=Be(t,e),e.isBefore(t)?n=tt(e,t):((n=tt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}function st(e,t){return function(n,s){var i,r;return null===s||isNaN(+s)||(S(t,"moment()."+t+"(period, number) is deprecated. Please use moment()."+t+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),r=n,n=s,s=r),n="string"==typeof n?+n:n,i=Ke(n,s),it(this,i,e),this}}function it(t,n,s,i){var r=n._milliseconds,a=$e(n._days),o=$e(n._months);t.isValid()&&(i=null==i||i,o&&K(t,q(t,"Month")+o*s),a&&J(t,"Date",q(t,"Date")+a*s),r&&t._d.setTime(t._d.valueOf()+r*s),i&&e.updateOffset(t,a||o))}function rt(e,t){var n,s=12*(t.year()-e.year())+(t.month()-e.month()),i=e.clone().add(s,"months");return n=t-i<0?(t-i)/(i-e.clone().add(s-1,"months")):(t-i)/(e.clone().add(s+1,"months")-i),-(s+n)||0}function at(e){var t;return void 0===e?this._locale._abbr:(null!=(t=ve(e))&&(this._locale=t),this)}function ot(){return this._locale}function ut(e,t){C(0,[e,e.length],0,t)}function lt(e,t,n,s,i){var r;return null==e?ae(this,s,i).year:(r=oe(e,s,i),t>r&&(t=r),dt.call(this,e,t,n,s,i))}function dt(e,t,n,s,i){var r=re(e,t,n,s,i),a=se(r.year,0,r.dayOfYear);return this.year(a.getUTCFullYear()),this.month(a.getUTCMonth()),this.date(a.getUTCDate()),this}function ht(e){return e}function ct(e,t,n,s){var i=ve(),r=d().set(s,t);return i[n](r,e)}function ft(e,t,n){if(r(e)&&(t=e,e=void 0),e=e||"",null!=t)return ct(e,t,n,"month");var s,i=[];for(s=0;s<12;s++)i[s]=ct(e,s,n,"month");return i}function mt(e,t,n,s){"boolean"==typeof e?(r(t)&&(n=t,t=void 0),t=t||""):(n=t=e,e=!1,r(t)&&(n=t,t=void 0),t=t||"");var i=ve(),a=e?i._week.dow:0;if(null!=n)return ct(t,(n+a)%7,s,"day");var o,u=[];for(o=0;o<7;o++)u[o]=ct(t,(o+a)%7,s,"day");return u}function _t(e,t,n,s){var i=Ke(t,n);return e._milliseconds+=s*i._milliseconds,e._days+=s*i._days,e._months+=s*i._months,e._bubble()}function yt(e){return e<0?Math.floor(e):Math.ceil(e)}function gt(e){return 4800*e/146097}function pt(e){return 146097*e/4800}function wt(e){return function(){return this.as(e)}}function vt(e){return function(){return this.isValid()?this._data[e]:NaN}}function Mt(e,t,n,s,i){return i.relativeTime(t||1,!!n,e,s)}function kt(e,t,n){var s=Ke(e).abs(),i=ks(s.as("s")),r=ks(s.as("m")),a=ks(s.as("h")),o=ks(s.as("d")),u=ks(s.as("M")),l=ks(s.as("y")),d=i<=Ss.ss&&["s",i]||i<Ss.s&&["ss",i]||r<=1&&["m"]||r<Ss.m&&["mm",r]||a<=1&&["h"]||a<Ss.h&&["hh",a]||o<=1&&["d"]||o<Ss.d&&["dd",o]||u<=1&&["M"]||u<Ss.M&&["MM",u]||l<=1&&["y"]||["yy",l];return d[2]=t,d[3]=+e>0,d[4]=n,Mt.apply(null,d)}function St(e){return(e>0)-(e<0)||+e}function Dt(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,s=Ds(this._milliseconds)/1e3,i=Ds(this._days),r=Ds(this._months);t=p((e=p(s/60))/60),s%=60,e%=60;var a=n=p(r/12),o=r%=12,u=i,l=t,d=e,h=s?s.toFixed(3).replace(/\.?0+$/,""):"",c=this.asSeconds();if(!c)return"P0D";var f=c<0?"-":"",m=St(this._months)!==St(c)?"-":"",_=St(this._days)!==St(c)?"-":"",y=St(this._milliseconds)!==St(c)?"-":"";return f+"P"+(a?m+a+"Y":"")+(o?m+o+"M":"")+(u?_+u+"D":"")+(l||d||h?"T":"")+(l?y+l+"H":"")+(d?y+d+"M":"")+(h?y+h+"S":"")}var Yt,Ot;Ot=Array.prototype.some?Array.prototype.some:function(e){for(var t=Object(this),n=t.length>>>0,s=0;s<n;s++)if(s in t&&e.call(this,t[s],s,t))return!0;return!1};var xt=e.momentProperties=[],Tt=!1,bt={};e.suppressDeprecationWarnings=!1,e.deprecationHandler=null;var Pt;Pt=Object.keys?Object.keys:function(e){var t,n=[];for(t in e)u(e,t)&&n.push(t);return n};var Wt={sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},Rt={LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},Ct=/\d{1,2}/,Ft={future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},Ut={},Nt={},Ht=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,Lt=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,Gt={},Vt={},jt=/\d/,It=/\d\d/,Et=/\d{3}/,At=/\d{4}/,zt=/[+-]?\d{6}/,Zt=/\d\d?/,$t=/\d\d\d\d?/,qt=/\d\d\d\d\d\d?/,Jt=/\d{1,3}/,Bt=/\d{1,4}/,Qt=/[+-]?\d{1,6}/,Xt=/\d+/,Kt=/[+-]?\d+/,en=/Z|[+-]\d\d:?\d\d/gi,tn=/Z|[+-]\d\d(?::?\d\d)?/gi,nn=/[+-]?\d+(\.\d{1,3})?/,sn=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,rn={},an={},on=0,un=1,ln=2,dn=3,hn=4,cn=5,fn=6,mn=7,_n=8;C("Y",0,0,function(){var e=this.year();return e<=9999?""+e:"+"+e}),C(0,["YY",2],0,function(){return this.year()%100}),C(0,["YYYY",4],0,"year"),C(0,["YYYYY",5],0,"year"),C(0,["YYYYYY",6,!0],0,"year"),x("year","y"),P("year",1),L("Y",Kt),L("YY",Zt,It),L("YYYY",Bt,At),L("YYYYY",Qt,zt),L("YYYYYY",Qt,zt),I(["YYYYY","YYYYYY"],on),I("YYYY",function(t,n){n[on]=2===t.length?e.parseTwoDigitYear(t):w(t)}),I("YY",function(t,n){n[on]=e.parseTwoDigitYear(t)}),I("Y",function(e,t){t[on]=parseInt(e,10)}),e.parseTwoDigitYear=function(e){return w(e)+(w(e)>68?1900:2e3)};var yn,gn=$("FullYear",!0);yn=Array.prototype.indexOf?Array.prototype.indexOf:function(e){var t;for(t=0;t<this.length;++t)if(this[t]===e)return t;return-1},C("M",["MM",2],"Mo",function(){return this.month()+1}),C("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),C("MMMM",0,0,function(e){return this.localeData().months(this,e)}),x("month","M"),P("month",8),L("M",Zt),L("MM",Zt,It),L("MMM",function(e,t){return t.monthsShortRegex(e)}),L("MMMM",function(e,t){return t.monthsRegex(e)}),I(["M","MM"],function(e,t){t[un]=w(e)-1}),I(["MMM","MMMM"],function(e,t,n,s){var i=n._locale.monthsParse(e,s,n._strict);null!=i?t[un]=i:c(n).invalidMonth=e});var pn=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,wn="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),vn="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),Mn=sn,kn=sn;C("w",["ww",2],"wo","week"),C("W",["WW",2],"Wo","isoWeek"),x("week","w"),x("isoWeek","W"),P("week",5),P("isoWeek",5),L("w",Zt),L("ww",Zt,It),L("W",Zt),L("WW",Zt,It),E(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=w(e)});var Sn={dow:0,doy:6};C("d",0,"do","day"),C("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),C("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),C("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),C("e",0,0,"weekday"),C("E",0,0,"isoWeekday"),x("day","d"),x("weekday","e"),x("isoWeekday","E"),P("day",11),P("weekday",11),P("isoWeekday",11),L("d",Zt),L("e",Zt),L("E",Zt),L("dd",function(e,t){return t.weekdaysMinRegex(e)}),L("ddd",function(e,t){return t.weekdaysShortRegex(e)}),L("dddd",function(e,t){return t.weekdaysRegex(e)}),E(["dd","ddd","dddd"],function(e,t,n,s){var i=n._locale.weekdaysParse(e,s,n._strict);null!=i?t.d=i:c(n).invalidWeekday=e}),E(["d","e","E"],function(e,t,n,s){t[s]=w(e)});var Dn="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Yn="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),On="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),xn=sn,Tn=sn,bn=sn;C("H",["HH",2],0,"hour"),C("h",["hh",2],0,ce),C("k",["kk",2],0,function(){return this.hours()||24}),C("hmm",0,0,function(){return""+ce.apply(this)+R(this.minutes(),2)}),C("hmmss",0,0,function(){return""+ce.apply(this)+R(this.minutes(),2)+R(this.seconds(),2)}),C("Hmm",0,0,function(){return""+this.hours()+R(this.minutes(),2)}),C("Hmmss",0,0,function(){return""+this.hours()+R(this.minutes(),2)+R(this.seconds(),2)}),fe("a",!0),fe("A",!1),x("hour","h"),P("hour",13),L("a",me),L("A",me),L("H",Zt),L("h",Zt),L("k",Zt),L("HH",Zt,It),L("hh",Zt,It),L("kk",Zt,It),L("hmm",$t),L("hmmss",qt),L("Hmm",$t),L("Hmmss",qt),I(["H","HH"],dn),I(["k","kk"],function(e,t,n){var s=w(e);t[dn]=24===s?0:s}),I(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),I(["h","hh"],function(e,t,n){t[dn]=w(e),c(n).bigHour=!0}),I("hmm",function(e,t,n){var s=e.length-2;t[dn]=w(e.substr(0,s)),t[hn]=w(e.substr(s)),c(n).bigHour=!0}),I("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[dn]=w(e.substr(0,s)),t[hn]=w(e.substr(s,2)),t[cn]=w(e.substr(i)),c(n).bigHour=!0}),I("Hmm",function(e,t,n){var s=e.length-2;t[dn]=w(e.substr(0,s)),t[hn]=w(e.substr(s))}),I("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[dn]=w(e.substr(0,s)),t[hn]=w(e.substr(s,2)),t[cn]=w(e.substr(i))});var Pn,Wn=/[ap]\.?m?\.?/i,Rn=$("Hours",!0),Cn={calendar:Wt,longDateFormat:Rt,invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:Ct,relativeTime:Ft,months:wn,monthsShort:vn,week:Sn,weekdays:Dn,weekdaysMin:On,weekdaysShort:Yn,meridiemParse:Wn},Fn={},Un={},Nn=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Hn=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Ln=/Z|[+-]\d\d(?::?\d\d)?/,Gn=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],Vn=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],jn=/^\/?Date\((\-?\d+)/i,In=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,En={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};e.createFromInputFallback=k("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(e){e._d=new Date(e._i+(e._useUTC?" UTC":""))}),e.ISO_8601=function(){},e.RFC_2822=function(){};var An=k("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=Ie.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:m()}),zn=k("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=Ie.apply(null,arguments);return this.isValid()&&e.isValid()?e>this?this:e:m()}),Zn=["year","quarter","month","week","day","hour","minute","second","millisecond"];qe("Z",":"),qe("ZZ",""),L("Z",tn),L("ZZ",tn),I(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Je(tn,e)});var $n=/([\+\-]|\d\d)/gi;e.updateOffset=function(){};var qn=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Jn=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;Ke.fn=ze.prototype,Ke.invalid=function(){return Ke(NaN)};var Bn=st(1,"add"),Qn=st(-1,"subtract");e.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",e.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var Xn=k("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});C(0,["gg",2],0,function(){return this.weekYear()%100}),C(0,["GG",2],0,function(){return this.isoWeekYear()%100}),ut("gggg","weekYear"),ut("ggggg","weekYear"),ut("GGGG","isoWeekYear"),ut("GGGGG","isoWeekYear"),x("weekYear","gg"),x("isoWeekYear","GG"),P("weekYear",1),P("isoWeekYear",1),L("G",Kt),L("g",Kt),L("GG",Zt,It),L("gg",Zt,It),L("GGGG",Bt,At),L("gggg",Bt,At),L("GGGGG",Qt,zt),L("ggggg",Qt,zt),E(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=w(e)}),E(["gg","GG"],function(t,n,s,i){n[i]=e.parseTwoDigitYear(t)}),C("Q",0,"Qo","quarter"),x("quarter","Q"),P("quarter",7),L("Q",jt),I("Q",function(e,t){t[un]=3*(w(e)-1)}),C("D",["DD",2],"Do","date"),x("date","D"),P("date",9),L("D",Zt),L("DD",Zt,It),L("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),I(["D","DD"],ln),I("Do",function(e,t){t[ln]=w(e.match(Zt)[0],10)});var Kn=$("Date",!0);C("DDD",["DDDD",3],"DDDo","dayOfYear"),x("dayOfYear","DDD"),P("dayOfYear",4),L("DDD",Jt),L("DDDD",Et),I(["DDD","DDDD"],function(e,t,n){n._dayOfYear=w(e)}),C("m",["mm",2],0,"minute"),x("minute","m"),P("minute",14),L("m",Zt),L("mm",Zt,It),I(["m","mm"],hn);var es=$("Minutes",!1);C("s",["ss",2],0,"second"),x("second","s"),P("second",15),L("s",Zt),L("ss",Zt,It),I(["s","ss"],cn);var ts=$("Seconds",!1);C("S",0,0,function(){return~~(this.millisecond()/100)}),C(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),C(0,["SSS",3],0,"millisecond"),C(0,["SSSS",4],0,function(){return 10*this.millisecond()}),C(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),C(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),C(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),C(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),C(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),x("millisecond","ms"),P("millisecond",16),L("S",Jt,jt),L("SS",Jt,It),L("SSS",Jt,Et);var ns;for(ns="SSSS";ns.length<=9;ns+="S")L(ns,Xt);for(ns="S";ns.length<=9;ns+="S")I(ns,function(e,t){t[fn]=w(1e3*("0."+e))});var ss=$("Milliseconds",!1);C("z",0,0,"zoneAbbr"),C("zz",0,0,"zoneName");var is=y.prototype;is.add=Bn,is.calendar=function(t,n){var s=t||Ie(),i=Be(s,this).startOf("day"),r=e.calendarFormat(this,i)||"sameElse",a=n&&(D(n[r])?n[r].call(this,s):n[r]);return this.format(a||this.localeData().calendar(r,this,Ie(s)))},is.clone=function(){return new y(this)},is.diff=function(e,t,n){var s,i,r;if(!this.isValid())return NaN;if(!(s=Be(e,this)).isValid())return NaN;switch(i=6e4*(s.utcOffset()-this.utcOffset()),t=T(t)){case"year":r=rt(this,s)/12;break;case"month":r=rt(this,s);break;case"quarter":r=rt(this,s)/3;break;case"second":r=(this-s)/1e3;break;case"minute":r=(this-s)/6e4;break;case"hour":r=(this-s)/36e5;break;case"day":r=(this-s-i)/864e5;break;case"week":r=(this-s-i)/6048e5;break;default:r=this-s}return n?r:p(r)},is.endOf=function(e){return void 0===(e=T(e))||"millisecond"===e?this:("date"===e&&(e="day"),this.startOf(e).add(1,"isoWeek"===e?"week":e).subtract(1,"ms"))},is.format=function(t){t||(t=this.isUtc()?e.defaultFormatUtc:e.defaultFormat);var n=N(this,t);return this.localeData().postformat(n)},is.from=function(e,t){return this.isValid()&&(g(e)&&e.isValid()||Ie(e).isValid())?Ke({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},is.fromNow=function(e){return this.from(Ie(),e)},is.to=function(e,t){return this.isValid()&&(g(e)&&e.isValid()||Ie(e).isValid())?Ke({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},is.toNow=function(e){return this.to(Ie(),e)},is.get=function(e){return e=T(e),D(this[e])?this[e]():this},is.invalidAt=function(){return c(this).overflow},is.isAfter=function(e,t){var n=g(e)?e:Ie(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=T(i(t)?"millisecond":t))?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(t).valueOf())},is.isBefore=function(e,t){var n=g(e)?e:Ie(e);return!(!this.isValid()||!n.isValid())&&("millisecond"===(t=T(i(t)?"millisecond":t))?this.valueOf()<n.valueOf():this.clone().endOf(t).valueOf()<n.valueOf())},is.isBetween=function(e,t,n,s){return("("===(s=s||"()")[0]?this.isAfter(e,n):!this.isBefore(e,n))&&(")"===s[1]?this.isBefore(t,n):!this.isAfter(t,n))},is.isSame=function(e,t){var n,s=g(e)?e:Ie(e);return!(!this.isValid()||!s.isValid())&&("millisecond"===(t=T(t||"millisecond"))?this.valueOf()===s.valueOf():(n=s.valueOf(),this.clone().startOf(t).valueOf()<=n&&n<=this.clone().endOf(t).valueOf()))},is.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},is.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},is.isValid=function(){return f(this)},is.lang=Xn,is.locale=at,is.localeData=ot,is.max=zn,is.min=An,is.parsingFlags=function(){return l({},c(this))},is.set=function(e,t){if("object"==typeof e)for(var n=W(e=b(e)),s=0;s<n.length;s++)this[n[s].unit](e[n[s].unit]);else if(e=T(e),D(this[e]))return this[e](t);return this},is.startOf=function(e){switch(e=T(e)){case"year":this.month(0);case"quarter":case"month":this.date(1);case"week":case"isoWeek":case"day":case"date":this.hours(0);case"hour":this.minutes(0);case"minute":this.seconds(0);case"second":this.milliseconds(0)}return"week"===e&&this.weekday(0),"isoWeek"===e&&this.isoWeekday(1),"quarter"===e&&this.month(3*Math.floor(this.month()/3)),this},is.subtract=Qn,is.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},is.toObject=function(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}},is.toDate=function(){return new Date(this.valueOf())},is.toISOString=function(){if(!this.isValid())return null;var e=this.clone().utc();return e.year()<0||e.year()>9999?N(e,"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]"):D(Date.prototype.toISOString)?this.toDate().toISOString():N(e,"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},is.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e="moment",t="";this.isLocal()||(e=0===this.utcOffset()?"moment.utc":"moment.parseZone",t="Z");var n="["+e+'("]',s=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=t+'[")]';return this.format(n+s+"-MM-DD[T]HH:mm:ss.SSS"+i)},is.toJSON=function(){return this.isValid()?this.toISOString():null},is.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},is.unix=function(){return Math.floor(this.valueOf()/1e3)},is.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},is.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},is.year=gn,is.isLeapYear=function(){return Z(this.year())},is.weekYear=function(e){return lt.call(this,e,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},is.isoWeekYear=function(e){return lt.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},is.quarter=is.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},is.month=ee,is.daysInMonth=function(){return Q(this.year(),this.month())},is.week=is.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},is.isoWeek=is.isoWeeks=function(e){var t=ae(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},is.weeksInYear=function(){var e=this.localeData()._week;return oe(this.year(),e.dow,e.doy)},is.isoWeeksInYear=function(){return oe(this.year(),1,4)},is.date=Kn,is.day=is.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=e?(e=ue(e,this.localeData()),this.add(e-t,"d")):t},is.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")},is.isoWeekday=function(e){if(!this.isValid())return null!=e?this:NaN;if(null!=e){var t=le(e,this.localeData());return this.day(this.day()%7?t:t-7)}return this.day()||7},is.dayOfYear=function(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")},is.hour=is.hours=Rn,is.minute=is.minutes=es,is.second=is.seconds=ts,is.millisecond=is.milliseconds=ss,is.utcOffset=function(t,n,s){var i,r=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null!=t){if("string"==typeof t){if(null===(t=Je(tn,t)))return this}else Math.abs(t)<16&&!s&&(t*=60);return!this._isUTC&&n&&(i=Qe(this)),this._offset=t,this._isUTC=!0,null!=i&&this.add(i,"m"),r!==t&&(!n||this._changeInProgress?it(this,Ke(t-r,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,e.updateOffset(this,!0),this._changeInProgress=null)),this}return this._isUTC?r:Qe(this)},is.utc=function(e){return this.utcOffset(0,e)},is.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Qe(this),"m")),this},is.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var e=Je(en,this._i);null!=e?this.utcOffset(e):this.utcOffset(0,!0)}return this},is.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?Ie(e).utcOffset():0,(this.utcOffset()-e)%60==0)},is.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},is.isLocal=function(){return!!this.isValid()&&!this._isUTC},is.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},is.isUtc=Xe,is.isUTC=Xe,is.zoneAbbr=function(){return this._isUTC?"UTC":""},is.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},is.dates=k("dates accessor is deprecated. Use date instead.",Kn),is.months=k("months accessor is deprecated. Use month instead",ee),is.years=k("years accessor is deprecated. Use year instead",gn),is.zone=k("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?("string"!=typeof e&&(e=-e),this.utcOffset(e,t),this):-this.utcOffset()}),is.isDSTShifted=k("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!i(this._isDSTShifted))return this._isDSTShifted;var e={};if(_(e,this),(e=Ge(e))._a){var t=e._isUTC?d(e._a):Ie(e._a);this._isDSTShifted=this.isValid()&&v(e._a,t.toArray())>0}else this._isDSTShifted=!1;return this._isDSTShifted});var rs=O.prototype;rs.calendar=function(e,t,n){var s=this._calendar[e]||this._calendar.sameElse;return D(s)?s.call(t,n):s},rs.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.replace(/MMMM|MM|DD|dddd/g,function(e){return e.slice(1)}),this._longDateFormat[e])},rs.invalidDate=function(){return this._invalidDate},rs.ordinal=function(e){return this._ordinal.replace("%d",e)},rs.preparse=ht,rs.postformat=ht,rs.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return D(i)?i(e,t,n,s):i.replace(/%d/i,e)},rs.pastFuture=function(e,t){var n=this._relativeTime[e>0?"future":"past"];return D(n)?n(t):n.replace(/%s/i,t)},rs.set=function(e){var t,n;for(n in e)D(t=e[n])?this[n]=t:this["_"+n]=t;this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},rs.months=function(e,n){return e?t(this._months)?this._months[e.month()]:this._months[(this._months.isFormat||pn).test(n)?"format":"standalone"][e.month()]:t(this._months)?this._months:this._months.standalone},rs.monthsShort=function(e,n){return e?t(this._monthsShort)?this._monthsShort[e.month()]:this._monthsShort[pn.test(n)?"format":"standalone"][e.month()]:t(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},rs.monthsParse=function(e,t,n){var s,i,r;if(this._monthsParseExact)return X.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(i=d([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(r="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(r.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[s].test(e))return s;if(n&&"MMM"===t&&this._shortMonthsParse[s].test(e))return s;if(!n&&this._monthsParse[s].test(e))return s}},rs.monthsRegex=function(e){return this._monthsParseExact?(u(this,"_monthsRegex")||te.call(this),e?this._monthsStrictRegex:this._monthsRegex):(u(this,"_monthsRegex")||(this._monthsRegex=kn),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},rs.monthsShortRegex=function(e){return this._monthsParseExact?(u(this,"_monthsRegex")||te.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(u(this,"_monthsShortRegex")||(this._monthsShortRegex=Mn),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},rs.week=function(e){return ae(e,this._week.dow,this._week.doy).week},rs.firstDayOfYear=function(){return this._week.doy},rs.firstDayOfWeek=function(){return this._week.dow},rs.weekdays=function(e,n){return e?t(this._weekdays)?this._weekdays[e.day()]:this._weekdays[this._weekdays.isFormat.test(n)?"format":"standalone"][e.day()]:t(this._weekdays)?this._weekdays:this._weekdays.standalone},rs.weekdaysMin=function(e){return e?this._weekdaysMin[e.day()]:this._weekdaysMin},rs.weekdaysShort=function(e){return e?this._weekdaysShort[e.day()]:this._weekdaysShort},rs.weekdaysParse=function(e,t,n){var s,i,r;if(this._weekdaysParseExact)return de.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(i=d([2e3,1]).day(s),n&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=new RegExp("^"+this.weekdays(i,"").replace(".",".?")+"$","i"),this._shortWeekdaysParse[s]=new RegExp("^"+this.weekdaysShort(i,"").replace(".",".?")+"$","i"),this._minWeekdaysParse[s]=new RegExp("^"+this.weekdaysMin(i,"").replace(".",".?")+"$","i")),this._weekdaysParse[s]||(r="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[s]=new RegExp(r.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[s].test(e))return s;if(n&&"ddd"===t&&this._shortWeekdaysParse[s].test(e))return s;if(n&&"dd"===t&&this._minWeekdaysParse[s].test(e))return s;if(!n&&this._weekdaysParse[s].test(e))return s}},rs.weekdaysRegex=function(e){return this._weekdaysParseExact?(u(this,"_weekdaysRegex")||he.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(u(this,"_weekdaysRegex")||(this._weekdaysRegex=xn),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},rs.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(u(this,"_weekdaysRegex")||he.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(u(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Tn),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},rs.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(u(this,"_weekdaysRegex")||he.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(u(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=bn),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},rs.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},rs.meridiem=function(e,t,n){return e>11?n?"pm":"PM":n?"am":"AM"},pe("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===w(e%100/10)?"th":1===t?"st":2===t?"nd":3===t?"rd":"th")}}),e.lang=k("moment.lang is deprecated. Use moment.locale instead.",pe),e.langData=k("moment.langData is deprecated. Use moment.localeData instead.",ve);var as=Math.abs,os=wt("ms"),us=wt("s"),ls=wt("m"),ds=wt("h"),hs=wt("d"),cs=wt("w"),fs=wt("M"),ms=wt("y"),_s=vt("milliseconds"),ys=vt("seconds"),gs=vt("minutes"),ps=vt("hours"),ws=vt("days"),vs=vt("months"),Ms=vt("years"),ks=Math.round,Ss={ss:44,s:45,m:45,h:22,d:26,M:11},Ds=Math.abs,Ys=ze.prototype;return Ys.isValid=function(){return this._isValid},Ys.abs=function(){var e=this._data;return this._milliseconds=as(this._milliseconds),this._days=as(this._days),this._months=as(this._months),e.milliseconds=as(e.milliseconds),e.seconds=as(e.seconds),e.minutes=as(e.minutes),e.hours=as(e.hours),e.months=as(e.months),e.years=as(e.years),this},Ys.add=function(e,t){return _t(this,e,t,1)},Ys.subtract=function(e,t){return _t(this,e,t,-1)},Ys.as=function(e){if(!this.isValid())return NaN;var t,n,s=this._milliseconds;if("month"===(e=T(e))||"year"===e)return t=this._days+s/864e5,n=this._months+gt(t),"month"===e?n:n/12;switch(t=this._days+Math.round(pt(this._months)),e){case"week":return t/7+s/6048e5;case"day":return t+s/864e5;case"hour":return 24*t+s/36e5;case"minute":return 1440*t+s/6e4;case"second":return 86400*t+s/1e3;case"millisecond":return Math.floor(864e5*t)+s;default:throw new Error("Unknown unit "+e)}},Ys.asMilliseconds=os,Ys.asSeconds=us,Ys.asMinutes=ls,Ys.asHours=ds,Ys.asDays=hs,Ys.asWeeks=cs,Ys.asMonths=fs,Ys.asYears=ms,Ys.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*w(this._months/12):NaN},Ys._bubble=function(){var e,t,n,s,i,r=this._milliseconds,a=this._days,o=this._months,u=this._data;return r>=0&&a>=0&&o>=0||r<=0&&a<=0&&o<=0||(r+=864e5*yt(pt(o)+a),a=0,o=0),u.milliseconds=r%1e3,e=p(r/1e3),u.seconds=e%60,t=p(e/60),u.minutes=t%60,n=p(t/60),u.hours=n%24,a+=p(n/24),i=p(gt(a)),o+=i,a-=yt(pt(i)),s=p(o/12),o%=12,u.days=a,u.months=o,u.years=s,this},Ys.clone=function(){return Ke(this)},Ys.get=function(e){return e=T(e),this.isValid()?this[e+"s"]():NaN},Ys.milliseconds=_s,Ys.seconds=ys,Ys.minutes=gs,Ys.hours=ps,Ys.days=ws,Ys.weeks=function(){return p(this.days()/7)},Ys.months=vs,Ys.years=Ms,Ys.humanize=function(e){if(!this.isValid())return this.localeData().invalidDate();var t=this.localeData(),n=kt(this,!e,t);return e&&(n=t.pastFuture(+this,n)),t.postformat(n)},Ys.toISOString=Dt,Ys.toString=Dt,Ys.toJSON=Dt,Ys.locale=at,Ys.localeData=ot,Ys.toIsoString=k("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Dt),Ys.lang=Xn,C("X",0,0,"unix"),C("x",0,0,"valueOf"),L("x",Kt),L("X",nn),I("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e,10))}),I("x",function(e,t,n){n._d=new Date(w(e))}),e.version="2.19.1",function(e){Yt=e}(Ie),e.fn=is,e.min=function(){return Ee("isBefore",[].slice.call(arguments,0))},e.max=function(){return Ee("isAfter",[].slice.call(arguments,0))},e.now=function(){return Date.now?Date.now():+new Date},e.utc=d,e.unix=function(e){return Ie(1e3*e)},e.months=function(e,t){return ft(e,t,"months")},e.isDate=a,e.locale=pe,e.invalid=m,e.duration=Ke,e.isMoment=g,e.weekdays=function(e,t,n){return mt(e,t,n,"weekdays")},e.parseZone=function(){return Ie.apply(null,arguments).parseZone()},e.localeData=ve,e.isDuration=Ze,e.monthsShort=function(e,t){return ft(e,t,"monthsShort")},e.weekdaysMin=function(e,t,n){return mt(e,t,n,"weekdaysMin")},e.defineLocale=we,e.updateLocale=function(e,t){if(null!=t){var n,s=Cn;null!=Fn[e]&&(s=Fn[e]._config),(n=new O(t=Y(s,t))).parentLocale=Fn[e],Fn[e]=n,pe(e)}else null!=Fn[e]&&(null!=Fn[e].parentLocale?Fn[e]=Fn[e].parentLocale:null!=Fn[e]&&delete Fn[e]);return Fn[e]},e.locales=function(){return Pt(Fn)},e.weekdaysShort=function(e,t,n){return mt(e,t,n,"weekdaysShort")},e.normalizeUnits=T,e.relativeTimeRounding=function(e){return void 0===e?ks:"function"==typeof e&&(ks=e,!0)},e.relativeTimeThreshold=function(e,t){return void 0!==Ss[e]&&(void 0===t?Ss[e]:(Ss[e]=t,"s"===e&&(Ss.ss=t-1),!0))},e.calendarFormat=function(e,t){var n=e.diff(t,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},e.prototype=is,e});;
/*! version : 4.7.14
 =========================================================
 bootstrap-datetimejs
 https://github.com/Eonasdan/bootstrap-datetimepicker
 Copyright (c) 2015 Jonathan Peterson
 =========================================================
 */
!function(a){"use strict";if("function"==typeof define&&define.amd)define(["jquery","moment"],a);else if("object"==typeof exports)a(require("jquery"),require("moment"));else{if("undefined"==typeof jQuery)throw"bootstrap-datetimepicker requires jQuery to be loaded first";if("undefined"==typeof moment)throw"bootstrap-datetimepicker requires Moment.js to be loaded first";a(jQuery,moment)}}(function(a,b){"use strict";if(!b)throw new Error("bootstrap-datetimepicker requires Moment.js to be loaded first");var c=function(c,d){var e,f,g,h,i,j={},k=b().startOf("d"),l=k.clone(),m=!0,n=!1,o=!1,p=0,q=[{clsName:"days",navFnc:"M",navStep:1},{clsName:"months",navFnc:"y",navStep:1},{clsName:"years",navFnc:"y",navStep:10}],r=["days","months","years"],s=["top","bottom","auto"],t=["left","right","auto"],u=["default","top","bottom"],v={up:38,38:"up",down:40,40:"down",left:37,37:"left",right:39,39:"right",tab:9,9:"tab",escape:27,27:"escape",enter:13,13:"enter",pageUp:33,33:"pageUp",pageDown:34,34:"pageDown",shift:16,16:"shift",control:17,17:"control",space:32,32:"space",t:84,84:"t","delete":46,46:"delete"},w={},x=function(a){if("string"!=typeof a||a.length>1)throw new TypeError("isEnabled expects a single character string parameter");switch(a){case"y":return-1!==g.indexOf("Y");case"M":return-1!==g.indexOf("M");case"d":return-1!==g.toLowerCase().indexOf("d");case"h":case"H":return-1!==g.toLowerCase().indexOf("h");case"m":return-1!==g.indexOf("m");case"s":return-1!==g.indexOf("s");default:return!1}},y=function(){return x("h")||x("m")||x("s")},z=function(){return x("y")||x("M")||x("d")},A=function(){var b=a("<thead>").append(a("<tr>").append(a("<th>").addClass("prev").attr("data-action","previous").append(a("<span>").addClass(d.icons.previous))).append(a("<th>").addClass("picker-switch").attr("data-action","pickerSwitch").attr("colspan",d.calendarWeeks?"6":"5")).append(a("<th>").addClass("next").attr("data-action","next").append(a("<span>").addClass(d.icons.next)))),c=a("<tbody>").append(a("<tr>").append(a("<td>").attr("colspan",d.calendarWeeks?"8":"7")));return[a("<div>").addClass("datepicker-days").append(a("<table>").addClass("table-condensed").append(b).append(a("<tbody>"))),a("<div>").addClass("datepicker-months").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone())),a("<div>").addClass("datepicker-years").append(a("<table>").addClass("table-condensed").append(b.clone()).append(c.clone()))]},B=function(){var b=a("<tr>"),c=a("<tr>"),e=a("<tr>");return x("h")&&(b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","incrementHours").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-hour").attr("data-time-component","hours").attr("data-action","showHours"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","decrementHours").append(a("<span>").addClass(d.icons.down))))),x("m")&&(x("h")&&(b.append(a("<td>").addClass("separator")),c.append(a("<td>").addClass("separator").html(":")),e.append(a("<td>").addClass("separator"))),b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","incrementMinutes").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-minute").attr("data-time-component","minutes").attr("data-action","showMinutes"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","decrementMinutes").append(a("<span>").addClass(d.icons.down))))),x("s")&&(x("m")&&(b.append(a("<td>").addClass("separator")),c.append(a("<td>").addClass("separator").html(":")),e.append(a("<td>").addClass("separator"))),b.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","incrementSeconds").append(a("<span>").addClass(d.icons.up)))),c.append(a("<td>").append(a("<span>").addClass("timepicker-second").attr("data-time-component","seconds").attr("data-action","showSeconds"))),e.append(a("<td>").append(a("<a>").attr({href:"#",tabindex:"-1"}).addClass("btn").attr("data-action","decrementSeconds").append(a("<span>").addClass(d.icons.down))))),f||(b.append(a("<td>").addClass("separator")),c.append(a("<td>").append(a("<button>").addClass("btn btn-primary").attr("data-action","togglePeriod"))),e.append(a("<td>").addClass("separator"))),a("<div>").addClass("timepicker-picker").append(a("<table>").addClass("table-condensed").append([b,c,e]))},C=function(){var b=a("<div>").addClass("timepicker-hours").append(a("<table>").addClass("table-condensed")),c=a("<div>").addClass("timepicker-minutes").append(a("<table>").addClass("table-condensed")),d=a("<div>").addClass("timepicker-seconds").append(a("<table>").addClass("table-condensed")),e=[B()];return x("h")&&e.push(b),x("m")&&e.push(c),x("s")&&e.push(d),e},D=function(){var b=[];return d.showTodayButton&&b.push(a("<td>").append(a("<a>").attr("data-action","today").append(a("<span>").addClass(d.icons.today)))),!d.sideBySide&&z()&&y()&&b.push(a("<td>").append(a("<a>").attr("data-action","togglePicker").append(a("<span>").addClass(d.icons.time)))),d.showClear&&b.push(a("<td>").append(a("<a>").attr("data-action","clear").append(a("<span>").addClass(d.icons.clear)))),d.showClose&&b.push(a("<td>").append(a("<a>").attr("data-action","close").append(a("<span>").addClass(d.icons.close)))),a("<table>").addClass("table-condensed").append(a("<tbody>").append(a("<tr>").append(b)))},E=function(){var b=a("<div>").addClass("bootstrap-datetimepicker-widget dropdown-menu"),c=a("<div>").addClass("datepicker").append(A()),e=a("<div>").addClass("timepicker").append(C()),g=a("<ul>").addClass("list-unstyled"),h=a("<li>").addClass("picker-switch"+(d.collapse?" accordion-toggle":"")).append(D());return d.inline&&b.removeClass("dropdown-menu"),f&&b.addClass("usetwentyfour"),d.sideBySide&&z()&&y()?(b.addClass("timepicker-sbs"),b.append(a("<div>").addClass("row").append(c.addClass("col-sm-6")).append(e.addClass("col-sm-6"))),b.append(h),b):("top"===d.toolbarPlacement&&g.append(h),z()&&g.append(a("<li>").addClass(d.collapse&&y()?"collapse in":"").append(c)),"default"===d.toolbarPlacement&&g.append(h),y()&&g.append(a("<li>").addClass(d.collapse&&z()?"collapse":"").append(e)),"bottom"===d.toolbarPlacement&&g.append(h),b.append(g))},F=function(){var b,e={};return b=c.is("input")||d.inline?c.data():c.find("input").data(),b.dateOptions&&b.dateOptions instanceof Object&&(e=a.extend(!0,e,b.dateOptions)),a.each(d,function(a){var c="date"+a.charAt(0).toUpperCase()+a.slice(1);void 0!==b[c]&&(e[a]=b[c])}),e},G=function(){var b,e=(n||c).position(),f=(n||c).offset(),g=d.widgetPositioning.vertical,h=d.widgetPositioning.horizontal;if(d.widgetParent)b=d.widgetParent.append(o);else if(c.is("input"))b=c.parent().append(o);else{if(d.inline)return void(b=c.append(o));b=c,c.children().first().after(o)}if("auto"===g&&(g=f.top+1.5*o.height()>=a(window).height()+a(window).scrollTop()&&o.height()+c.outerHeight()<f.top?"top":"bottom"),"auto"===h&&(h=b.width()<f.left+o.outerWidth()/2&&f.left+o.outerWidth()>a(window).width()?"right":"left"),"top"===g?o.addClass("top").removeClass("bottom"):o.addClass("bottom").removeClass("top"),"right"===h?o.addClass("pull-right"):o.removeClass("pull-right"),"relative"!==b.css("position")&&(b=b.parents().filter(function(){return"relative"===a(this).css("position")}).first()),0===b.length)throw new Error("datetimepicker component should be placed within a relative positioned container");o.css({top:"top"===g?"auto":e.top+c.outerHeight(),bottom:"top"===g?e.top+c.outerHeight():"auto",left:"left"===h?b.css("padding-left"):"auto",right:"left"===h?"auto":b.width()-c.outerWidth()})},H=function(a){"dp.change"===a.type&&(a.date&&a.date.isSame(a.oldDate)||!a.date&&!a.oldDate)||c.trigger(a)},I=function(a){o&&(a&&(i=Math.max(p,Math.min(2,i+a))),o.find(".datepicker > div").hide().filter(".datepicker-"+q[i].clsName).show())},J=function(){var b=a("<tr>"),c=l.clone().startOf("w");for(d.calendarWeeks===!0&&b.append(a("<th>").addClass("cw").text("#"));c.isBefore(l.clone().endOf("w"));)b.append(a("<th>").addClass("dow").text(c.format("dd"))),c.add(1,"d");o.find(".datepicker-days thead").append(b)},K=function(a){return d.disabledDates[a.format("YYYY-MM-DD")]===!0},L=function(a){return d.enabledDates[a.format("YYYY-MM-DD")]===!0},M=function(a,b){return a.isValid()?d.disabledDates&&K(a)&&"M"!==b?!1:d.enabledDates&&!L(a)&&"M"!==b?!1:d.minDate&&a.isBefore(d.minDate,b)?!1:d.maxDate&&a.isAfter(d.maxDate,b)?!1:"d"===b&&-1!==d.daysOfWeekDisabled.indexOf(a.day())?!1:!0:!1},N=function(){for(var b=[],c=l.clone().startOf("y").hour(12);c.isSame(l,"y");)b.push(a("<span>").attr("data-action","selectMonth").addClass("month").text(c.format("MMM"))),c.add(1,"M");o.find(".datepicker-months td").empty().append(b)},O=function(){var b=o.find(".datepicker-months"),c=b.find("th"),d=b.find("tbody").find("span");b.find(".disabled").removeClass("disabled"),M(l.clone().subtract(1,"y"),"y")||c.eq(0).addClass("disabled"),c.eq(1).text(l.year()),M(l.clone().add(1,"y"),"y")||c.eq(2).addClass("disabled"),d.removeClass("active"),k.isSame(l,"y")&&d.eq(k.month()).addClass("active"),d.each(function(b){M(l.clone().month(b),"M")||a(this).addClass("disabled")})},P=function(){var a=o.find(".datepicker-years"),b=a.find("th"),c=l.clone().subtract(5,"y"),e=l.clone().add(6,"y"),f="";for(a.find(".disabled").removeClass("disabled"),d.minDate&&d.minDate.isAfter(c,"y")&&b.eq(0).addClass("disabled"),b.eq(1).text(c.year()+"-"+e.year()),d.maxDate&&d.maxDate.isBefore(e,"y")&&b.eq(2).addClass("disabled");!c.isAfter(e,"y");)f+='<span data-action="selectYear" class="year'+(c.isSame(k,"y")?" active":"")+(M(c,"y")?"":" disabled")+'">'+c.year()+"</span>",c.add(1,"y");a.find("td").html(f)},Q=function(){var c,e,f,g=o.find(".datepicker-days"),h=g.find("th"),i=[];if(z()){for(g.find(".disabled").removeClass("disabled"),h.eq(1).text(l.format(d.dayViewHeaderFormat)),M(l.clone().subtract(1,"M"),"M")||h.eq(0).addClass("disabled"),M(l.clone().add(1,"M"),"M")||h.eq(2).addClass("disabled"),c=l.clone().startOf("M").startOf("week");!l.clone().endOf("M").endOf("w").isBefore(c,"d");)0===c.weekday()&&(e=a("<tr>"),d.calendarWeeks&&e.append('<td class="cw">'+c.week()+"</td>"),i.push(e)),f="",c.isBefore(l,"M")&&(f+=" old"),c.isAfter(l,"M")&&(f+=" new"),c.isSame(k,"d")&&!m&&(f+=" active"),M(c,"d")||(f+=" disabled"),c.isSame(b(),"d")&&(f+=" today"),(0===c.day()||6===c.day())&&(f+=" weekend"),e.append('<td data-action="selectDay" class="day'+f+'">'+c.date()+"</td>"),c.add(1,"d");g.find("tbody").empty().append(i),O(),P()}},R=function(){var b=o.find(".timepicker-hours table"),c=l.clone().startOf("d"),d=[],e=a("<tr>");for(l.hour()>11&&!f&&c.hour(12);c.isSame(l,"d")&&(f||l.hour()<12&&c.hour()<12||l.hour()>11);)c.hour()%4===0&&(e=a("<tr>"),d.push(e)),e.append('<td data-action="selectHour" class="hour'+(M(c,"h")?"":" disabled")+'">'+c.format(f?"HH":"hh")+"</td>"),c.add(1,"h");b.empty().append(d)},S=function(){for(var b=o.find(".timepicker-minutes table"),c=l.clone().startOf("h"),e=[],f=a("<tr>"),g=1===d.stepping?5:d.stepping;l.isSame(c,"h");)c.minute()%(4*g)===0&&(f=a("<tr>"),e.push(f)),f.append('<td data-action="selectMinute" class="minute'+(M(c,"m")?"":" disabled")+'">'+c.format("mm")+"</td>"),c.add(g,"m");b.empty().append(e)},T=function(){for(var b=o.find(".timepicker-seconds table"),c=l.clone().startOf("m"),d=[],e=a("<tr>");l.isSame(c,"m");)c.second()%20===0&&(e=a("<tr>"),d.push(e)),e.append('<td data-action="selectSecond" class="second'+(M(c,"s")?"":" disabled")+'">'+c.format("ss")+"</td>"),c.add(5,"s");b.empty().append(d)},U=function(){var a=o.find(".timepicker span[data-time-component]");f||o.find(".timepicker [data-action=togglePeriod]").text(k.format("A")),a.filter("[data-time-component=hours]").text(k.format(f?"HH":"hh")),a.filter("[data-time-component=minutes]").text(k.format("mm")),a.filter("[data-time-component=seconds]").text(k.format("ss")),R(),S(),T()},V=function(){o&&(Q(),U())},W=function(a){var b=m?null:k;return a?(a=a.clone().locale(d.locale),1!==d.stepping&&a.minutes(Math.round(a.minutes()/d.stepping)*d.stepping%60).seconds(0),void(M(a)?(k=a,l=k.clone(),e.val(k.format(g)),c.data("date",k.format(g)),V(),m=!1,H({type:"dp.change",date:k.clone(),oldDate:b})):(d.keepInvalid||e.val(m?"":k.format(g)),H({type:"dp.error",date:a})))):(m=!0,e.val(""),c.data("date",""),H({type:"dp.change",date:null,oldDate:b}),void V())},X=function(){var b=!1;return o?(o.find(".collapse").each(function(){var c=a(this).data("collapse");return c&&c.transitioning?(b=!0,!1):!0}),b?j:(n&&n.hasClass("btn")&&n.toggleClass("active"),o.hide(),a(window).off("resize",G),o.off("click","[data-action]"),o.off("mousedown",!1),o.remove(),o=!1,H({type:"dp.hide",date:k.clone()}),j)):j},Y=function(){W(null)},Z={next:function(){l.add(q[i].navStep,q[i].navFnc),Q()},previous:function(){l.subtract(q[i].navStep,q[i].navFnc),Q()},pickerSwitch:function(){I(1)},selectMonth:function(b){var c=a(b.target).closest("tbody").find("span").index(a(b.target));l.month(c),i===p?(W(k.clone().year(l.year()).month(l.month())),d.inline||X()):(I(-1),Q())},selectYear:function(b){var c=parseInt(a(b.target).text(),10)||0;l.year(c),i===p?(W(k.clone().year(l.year())),d.inline||X()):(I(-1),Q())},selectDay:function(b){var c=l.clone();a(b.target).is(".old")&&c.subtract(1,"M"),a(b.target).is(".new")&&c.add(1,"M"),W(c.date(parseInt(a(b.target).text(),10))),y()||d.keepOpen||d.inline||X()},incrementHours:function(){W(k.clone().add(1,"h"))},incrementMinutes:function(){W(k.clone().add(d.stepping,"m"))},incrementSeconds:function(){W(k.clone().add(1,"s"))},decrementHours:function(){W(k.clone().subtract(1,"h"))},decrementMinutes:function(){W(k.clone().subtract(d.stepping,"m"))},decrementSeconds:function(){W(k.clone().subtract(1,"s"))},togglePeriod:function(){W(k.clone().add(k.hours()>=12?-12:12,"h"))},togglePicker:function(b){var c,e=a(b.target),f=e.closest("ul"),g=f.find(".in"),h=f.find(".collapse:not(.in)");if(g&&g.length){if(c=g.data("collapse"),c&&c.transitioning)return;g.collapse?(g.collapse("hide"),h.collapse("show")):(g.removeClass("in"),h.addClass("in")),e.is("span")?e.toggleClass(d.icons.time+" "+d.icons.date):e.find("span").toggleClass(d.icons.time+" "+d.icons.date)}},showPicker:function(){o.find(".timepicker > div:not(.timepicker-picker)").hide(),o.find(".timepicker .timepicker-picker").show()},showHours:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-hours").show()},showMinutes:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){o.find(".timepicker .timepicker-picker").hide(),o.find(".timepicker .timepicker-seconds").show()},selectHour:function(b){var c=parseInt(a(b.target).text(),10);f||(k.hours()>=12?12!==c&&(c+=12):12===c&&(c=0)),W(k.clone().hours(c)),Z.showPicker.call(j)},selectMinute:function(b){W(k.clone().minutes(parseInt(a(b.target).text(),10))),Z.showPicker.call(j)},selectSecond:function(b){W(k.clone().seconds(parseInt(a(b.target).text(),10))),Z.showPicker.call(j)},clear:Y,today:function(){W(b())},close:X},$=function(b){return a(b.currentTarget).is(".disabled")?!1:(Z[a(b.currentTarget).data("action")].apply(j,arguments),!1)},_=function(){var c,f={year:function(a){return a.month(0).date(1).hours(0).seconds(0).minutes(0)},month:function(a){return a.date(1).hours(0).seconds(0).minutes(0)},day:function(a){return a.hours(0).seconds(0).minutes(0)},hour:function(a){return a.seconds(0).minutes(0)},minute:function(a){return a.seconds(0)}};return e.prop("disabled")||!d.ignoreReadonly&&e.prop("readonly")||o?j:(d.useCurrent&&m&&(e.is("input")&&0===e.val().trim().length||d.inline)&&(c=b(),"string"==typeof d.useCurrent&&(c=f[d.useCurrent](c)),W(c)),o=E(),J(),N(),o.find(".timepicker-hours").hide(),o.find(".timepicker-minutes").hide(),o.find(".timepicker-seconds").hide(),V(),I(),a(window).on("resize",G),o.on("click","[data-action]",$),o.on("mousedown",!1),n&&n.hasClass("btn")&&n.toggleClass("active"),o.show(),G(),e.is(":focus")||e.focus(),H({type:"dp.show"}),j)},ab=function(){return o?X():_()},bb=function(a){return a=b.isMoment(a)||a instanceof Date?b(a):b(a,h,d.useStrict),a.locale(d.locale),a},cb=function(a){var b,c,e,f,g=null,h=[],i={},k=a.which,l="p";w[k]=l;for(b in w)w.hasOwnProperty(b)&&w[b]===l&&(h.push(b),parseInt(b,10)!==k&&(i[b]=!0));for(b in d.keyBinds)if(d.keyBinds.hasOwnProperty(b)&&"function"==typeof d.keyBinds[b]&&(e=b.split(" "),e.length===h.length&&v[k]===e[e.length-1])){for(f=!0,c=e.length-2;c>=0;c--)if(!(v[e[c]]in i)){f=!1;break}if(f){g=d.keyBinds[b];break}}g&&(g.call(j,o),a.stopPropagation(),a.preventDefault())},db=function(a){w[a.which]="r",a.stopPropagation(),a.preventDefault()},eb=function(b){var c=a(b.target).val().trim(),d=c?bb(c):null;return W(d),b.stopImmediatePropagation(),!1},fb=function(){e.on({change:eb,blur:d.debug?"":X,keydown:cb,keyup:db}),c.is("input")?e.on({focus:_}):n&&(n.on("click",ab),n.on("mousedown",!1))},gb=function(){e.off({change:eb,blur:X,keydown:cb,keyup:db}),c.is("input")?e.off({focus:_}):n&&(n.off("click",ab),n.off("mousedown",!1))},hb=function(b){var c={};return a.each(b,function(){var a=bb(this);a.isValid()&&(c[a.format("YYYY-MM-DD")]=!0)}),Object.keys(c).length?c:!1},ib=function(){var a=d.format||"L LT";g=a.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){var b=k.localeData().longDateFormat(a)||a;return b.replace(/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,function(a){return k.localeData().longDateFormat(a)||a})}),h=d.extraFormats?d.extraFormats.slice():[],h.indexOf(a)<0&&h.indexOf(g)<0&&h.push(g),f=g.toLowerCase().indexOf("a")<1&&g.indexOf("h")<1,x("y")&&(p=2),x("M")&&(p=1),x("d")&&(p=0),i=Math.max(p,i),m||W(k)};if(j.destroy=function(){X(),gb(),c.removeData("DateTimePicker"),c.removeData("date")},j.toggle=ab,j.show=_,j.hide=X,j.disable=function(){return X(),n&&n.hasClass("btn")&&n.addClass("disabled"),e.prop("disabled",!0),j},j.enable=function(){return n&&n.hasClass("btn")&&n.removeClass("disabled"),e.prop("disabled",!1),j},j.ignoreReadonly=function(a){if(0===arguments.length)return d.ignoreReadonly;if("boolean"!=typeof a)throw new TypeError("ignoreReadonly () expects a boolean parameter");return d.ignoreReadonly=a,j},j.options=function(b){if(0===arguments.length)return a.extend(!0,{},d);if(!(b instanceof Object))throw new TypeError("options() options parameter should be an object");return a.extend(!0,d,b),a.each(d,function(a,b){if(void 0===j[a])throw new TypeError("option "+a+" is not recognized!");j[a](b)}),j},j.date=function(a){if(0===arguments.length)return m?null:k.clone();if(!(null===a||"string"==typeof a||b.isMoment(a)||a instanceof Date))throw new TypeError("date() parameter must be one of [null, string, moment or Date]");return W(null===a?null:bb(a)),j},j.format=function(a){if(0===arguments.length)return d.format;if("string"!=typeof a&&("boolean"!=typeof a||a!==!1))throw new TypeError("format() expects a sting or boolean:false parameter "+a);return d.format=a,g&&ib(),j},j.dayViewHeaderFormat=function(a){if(0===arguments.length)return d.dayViewHeaderFormat;if("string"!=typeof a)throw new TypeError("dayViewHeaderFormat() expects a string parameter");return d.dayViewHeaderFormat=a,j},j.extraFormats=function(a){if(0===arguments.length)return d.extraFormats;if(a!==!1&&!(a instanceof Array))throw new TypeError("extraFormats() expects an array or false parameter");return d.extraFormats=a,h&&ib(),j},j.disabledDates=function(b){if(0===arguments.length)return d.disabledDates?a.extend({},d.disabledDates):d.disabledDates;if(!b)return d.disabledDates=!1,V(),j;if(!(b instanceof Array))throw new TypeError("disabledDates() expects an array parameter");return d.disabledDates=hb(b),d.enabledDates=!1,V(),j},j.enabledDates=function(b){if(0===arguments.length)return d.enabledDates?a.extend({},d.enabledDates):d.enabledDates;if(!b)return d.enabledDates=!1,V(),j;if(!(b instanceof Array))throw new TypeError("enabledDates() expects an array parameter");return d.enabledDates=hb(b),d.disabledDates=!1,V(),j},j.daysOfWeekDisabled=function(a){if(0===arguments.length)return d.daysOfWeekDisabled.splice(0);if(!(a instanceof Array))throw new TypeError("daysOfWeekDisabled() expects an array parameter");return d.daysOfWeekDisabled=a.reduce(function(a,b){return b=parseInt(b,10),b>6||0>b||isNaN(b)?a:(-1===a.indexOf(b)&&a.push(b),a)},[]).sort(),V(),j},j.maxDate=function(a){if(0===arguments.length)return d.maxDate?d.maxDate.clone():d.maxDate;if("boolean"==typeof a&&a===!1)return d.maxDate=!1,V(),j;"string"==typeof a&&("now"===a||"moment"===a)&&(a=b());var c=bb(a);if(!c.isValid())throw new TypeError("maxDate() Could not parse date parameter: "+a);if(d.minDate&&c.isBefore(d.minDate))throw new TypeError("maxDate() date parameter is before options.minDate: "+c.format(g));return d.maxDate=c,d.maxDate.isBefore(a)&&W(d.maxDate),l.isAfter(c)&&(l=c.clone()),V(),j},j.minDate=function(a){if(0===arguments.length)return d.minDate?d.minDate.clone():d.minDate;if("boolean"==typeof a&&a===!1)return d.minDate=!1,V(),j;"string"==typeof a&&("now"===a||"moment"===a)&&(a=b());var c=bb(a);if(!c.isValid())throw new TypeError("minDate() Could not parse date parameter: "+a);if(d.maxDate&&c.isAfter(d.maxDate))throw new TypeError("minDate() date parameter is after options.maxDate: "+c.format(g));return d.minDate=c,d.minDate.isAfter(a)&&W(d.minDate),l.isBefore(c)&&(l=c.clone()),V(),j},j.defaultDate=function(a){if(0===arguments.length)return d.defaultDate?d.defaultDate.clone():d.defaultDate;if(!a)return d.defaultDate=!1,j;"string"==typeof a&&("now"===a||"moment"===a)&&(a=b());var c=bb(a);if(!c.isValid())throw new TypeError("defaultDate() Could not parse date parameter: "+a);if(!M(c))throw new TypeError("defaultDate() date passed is invalid according to component setup validations");return d.defaultDate=c,d.defaultDate&&""===e.val().trim()&&void 0===e.attr("placeholder")&&W(d.defaultDate),j},j.locale=function(a){if(0===arguments.length)return d.locale;if(!b.localeData(a))throw new TypeError("locale() locale "+a+" is not loaded from moment locales!");return d.locale=a,k.locale(d.locale),l.locale(d.locale),g&&ib(),o&&(X(),_()),j},j.stepping=function(a){return 0===arguments.length?d.stepping:(a=parseInt(a,10),(isNaN(a)||1>a)&&(a=1),d.stepping=a,j)},j.useCurrent=function(a){var b=["year","month","day","hour","minute"];if(0===arguments.length)return d.useCurrent;if("boolean"!=typeof a&&"string"!=typeof a)throw new TypeError("useCurrent() expects a boolean or string parameter");if("string"==typeof a&&-1===b.indexOf(a.toLowerCase()))throw new TypeError("useCurrent() expects a string parameter of "+b.join(", "));return d.useCurrent=a,j},j.collapse=function(a){if(0===arguments.length)return d.collapse;if("boolean"!=typeof a)throw new TypeError("collapse() expects a boolean parameter");return d.collapse===a?j:(d.collapse=a,o&&(X(),_()),j)},j.icons=function(b){if(0===arguments.length)return a.extend({},d.icons);if(!(b instanceof Object))throw new TypeError("icons() expects parameter to be an Object");return a.extend(d.icons,b),o&&(X(),_()),j},j.useStrict=function(a){if(0===arguments.length)return d.useStrict;if("boolean"!=typeof a)throw new TypeError("useStrict() expects a boolean parameter");return d.useStrict=a,j},j.sideBySide=function(a){if(0===arguments.length)return d.sideBySide;if("boolean"!=typeof a)throw new TypeError("sideBySide() expects a boolean parameter");return d.sideBySide=a,o&&(X(),_()),j},j.viewMode=function(a){if(0===arguments.length)return d.viewMode;if("string"!=typeof a)throw new TypeError("viewMode() expects a string parameter");if(-1===r.indexOf(a))throw new TypeError("viewMode() parameter must be one of ("+r.join(", ")+") value");return d.viewMode=a,i=Math.max(r.indexOf(a),p),I(),j},j.toolbarPlacement=function(a){if(0===arguments.length)return d.toolbarPlacement;if("string"!=typeof a)throw new TypeError("toolbarPlacement() expects a string parameter");if(-1===u.indexOf(a))throw new TypeError("toolbarPlacement() parameter must be one of ("+u.join(", ")+") value");return d.toolbarPlacement=a,o&&(X(),_()),j},j.widgetPositioning=function(b){if(0===arguments.length)return a.extend({},d.widgetPositioning);if("[object Object]"!=={}.toString.call(b))throw new TypeError("widgetPositioning() expects an object variable");if(b.horizontal){if("string"!=typeof b.horizontal)throw new TypeError("widgetPositioning() horizontal variable must be a string");if(b.horizontal=b.horizontal.toLowerCase(),-1===t.indexOf(b.horizontal))throw new TypeError("widgetPositioning() expects horizontal parameter to be one of ("+t.join(", ")+")");d.widgetPositioning.horizontal=b.horizontal}if(b.vertical){if("string"!=typeof b.vertical)throw new TypeError("widgetPositioning() vertical variable must be a string");if(b.vertical=b.vertical.toLowerCase(),-1===s.indexOf(b.vertical))throw new TypeError("widgetPositioning() expects vertical parameter to be one of ("+s.join(", ")+")");d.widgetPositioning.vertical=b.vertical}return V(),j},j.calendarWeeks=function(a){if(0===arguments.length)return d.calendarWeeks;if("boolean"!=typeof a)throw new TypeError("calendarWeeks() expects parameter to be a boolean value");return d.calendarWeeks=a,V(),j},j.showTodayButton=function(a){if(0===arguments.length)return d.showTodayButton;if("boolean"!=typeof a)throw new TypeError("showTodayButton() expects a boolean parameter");return d.showTodayButton=a,o&&(X(),_()),j},j.showClear=function(a){if(0===arguments.length)return d.showClear;if("boolean"!=typeof a)throw new TypeError("showClear() expects a boolean parameter");return d.showClear=a,o&&(X(),_()),j},j.widgetParent=function(b){if(0===arguments.length)return d.widgetParent;if("string"==typeof b&&(b=a(b)),null!==b&&"string"!=typeof b&&!(b instanceof a))throw new TypeError("widgetParent() expects a string or a jQuery object parameter");return d.widgetParent=b,o&&(X(),_()),j},j.keepOpen=function(a){if(0===arguments.length)return d.keepOpen;if("boolean"!=typeof a)throw new TypeError("keepOpen() expects a boolean parameter");return d.keepOpen=a,j},j.inline=function(a){if(0===arguments.length)return d.inline;if("boolean"!=typeof a)throw new TypeError("inline() expects a boolean parameter");return d.inline=a,j},j.clear=function(){return Y(),j},j.keyBinds=function(a){return d.keyBinds=a,j},j.debug=function(a){if("boolean"!=typeof a)throw new TypeError("debug() expects a boolean parameter");return d.debug=a,j},j.showClose=function(a){if(0===arguments.length)return d.showClose;if("boolean"!=typeof a)throw new TypeError("showClose() expects a boolean parameter");return d.showClose=a,j},j.keepInvalid=function(a){if(0===arguments.length)return d.keepInvalid;if("boolean"!=typeof a)throw new TypeError("keepInvalid() expects a boolean parameter");return d.keepInvalid=a,j},j.datepickerInput=function(a){if(0===arguments.length)return d.datepickerInput;if("string"!=typeof a)throw new TypeError("datepickerInput() expects a string parameter");return d.datepickerInput=a,j},c.is("input"))e=c;else if(e=c.find(d.datepickerInput),0===e.size())e=c.find("input");else if(!e.is("input"))throw new Error('CSS class "'+d.datepickerInput+'" cannot be applied to non input element');if(c.hasClass("input-group")&&(n=c.find(0===c.find(".datepickerbutton").size()?'[class^="input-group-"]':".datepickerbutton")),!d.inline&&!e.is("input"))throw new Error("Could not initialize DateTimePicker without an input element");return a.extend(!0,d,F()),j.options(d),ib(),fb(),e.prop("disabled")&&j.disable(),e.is("input")&&0!==e.val().trim().length?W(bb(e.val().trim())):d.defaultDate&&void 0===e.attr("placeholder")&&W(d.defaultDate),d.inline&&_(),j};a.fn.datetimepicker=function(b){return this.each(function(){var d=a(this);d.data("DateTimePicker")||(b=a.extend(!0,{},a.fn.datetimepicker.defaults,b),d.data("DateTimePicker",c(d,b)))})},a.fn.datetimepicker.defaults={format:!1,dayViewHeaderFormat:"MMMM YYYY",extraFormats:!1,stepping:1,minDate:!1,maxDate:!1,useCurrent:!0,collapse:!0,locale:b.locale(),defaultDate:!1,disabledDates:!1,enabledDates:!1,icons:{time:"glyphicon glyphicon-time",date:"glyphicon glyphicon-calendar",up:"glyphicon glyphicon-chevron-up",down:"glyphicon glyphicon-chevron-down",previous:"glyphicon glyphicon-chevron-left",next:"glyphicon glyphicon-chevron-right",today:"glyphicon glyphicon-screenshot",clear:"glyphicon glyphicon-trash",close:"glyphicon glyphicon-remove"},useStrict:!1,sideBySide:!1,daysOfWeekDisabled:[],calendarWeeks:!1,viewMode:"days",toolbarPlacement:"default",showTodayButton:!1,showClear:!1,showClose:!1,widgetPositioning:{horizontal:"auto",vertical:"auto"},widgetParent:null,ignoreReadonly:!1,keepOpen:!1,inline:!1,keepInvalid:!1,datepickerInput:".datepickerinput",keyBinds:{up:function(a){if(a){var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().subtract(7,"d"):c.clone().add(1,"m"))}},down:function(a){if(!a)return void this.show();var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().add(7,"d"):c.clone().subtract(1,"m"))},"control up":function(a){if(a){var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().subtract(1,"y"):c.clone().add(1,"h"))}},"control down":function(a){if(a){var c=this.date()||b();this.date(a.find(".datepicker").is(":visible")?c.clone().add(1,"y"):c.clone().subtract(1,"h"))}},left:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().subtract(1,"d"))}},right:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().add(1,"d"))}},pageUp:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().subtract(1,"M"))}},pageDown:function(a){if(a){var c=this.date()||b();a.find(".datepicker").is(":visible")&&this.date(c.clone().add(1,"M"))}},enter:function(){this.hide()},escape:function(){this.hide()},"control space":function(a){a.find(".timepicker").is(":visible")&&a.find('.btn[data-action="togglePeriod"]').click()},t:function(){this.date(b())},"delete":function(){this.clear()}},debug:!1}});;
/*!
 * Add to Homescreen v2.0.11 ~ Copyright (c) 2013 Matteo Spinelli, http://cubiq.org
 * Released under MIT license, http://cubiq.org/license
 */
var addToHome = (function (w) {
	var nav = w.navigator,
		isIDevice = 'platform' in nav && (/iphone|ipod|ipad/gi).test(nav.platform),
		isIPad,
		isRetina,
		isSafari,
		isStandalone,
		OSVersion,
		startX = 0,
		startY = 0,
		lastVisit = 0,
		isExpired,
		isSessionActive,
		isReturningVisitor,
		balloon,
		overrideChecks,

		positionInterval,
		closeTimeout,

		options = {
			autostart: true,			// Automatically open the balloon
			returningVisitor: false,	// Show the balloon to returning visitors only (setting this to true is highly recommended)
			animationIn: 'drop',		// drop || bubble || fade
			animationOut: 'fade',		// drop || bubble || fade
			startDelay: 2000,			// 2 seconds from page load before the balloon appears
			lifespan: 15000,			// 15 seconds before it is automatically destroyed
			bottomOffset: 14,			// Distance of the balloon from bottom
			expire: 0,					// Minutes to wait before showing the popup again (0 = always displayed)
			message: '',				// Customize your message or force a language ('' = automatic)
			touchIcon: false,			// Display the touch icon
			arrow: true,				// Display the balloon arrow
			hookOnLoad: true,			// Should we hook to onload event? (really advanced usage)
			closeButton: true,			// Let the user close the balloon
			iterations: 100				// Internal/debug use
		},

		intl = {
			ar:    '<span dir="rtl">قم بتثبيت هذا التطبيق على <span dir="ltr">%device:</span>انقر<span dir="ltr">%icon</span> ،<strong>ثم اضفه الى الشاشة الرئيسية.</strong></span>',
			ca_es: 'Per instal·lar aquesta aplicació al vostre %device premeu %icon i llavors <strong>Afegir a pantalla d\'inici</strong>.',
			cs_cz: 'Pro instalaci aplikace na Váš %device, stiskněte %icon a v nabídce <strong>Přidat na plochu</strong>.',
			da_dk: 'Tilføj denne side til din %device: tryk på %icon og derefter <strong>Føj til hjemmeskærm</strong>.',
			de_de: 'Installieren Sie diese App auf Ihrem %device: %icon antippen und dann <strong>Zum Home-Bildschirm</strong>.',
			el_gr: 'Εγκαταστήσετε αυτήν την Εφαρμογή στήν συσκευή σας %device: %icon μετά πατάτε <strong>Προσθήκη σε Αφετηρία</strong>.',
			en_us: 'Install this web app on your %device: tap %icon and then <strong>Add to Home Screen</strong>.',
			es_es: 'Para instalar esta app en su %device, pulse %icon y seleccione <strong>Añadir a pantalla de inicio</strong>.',
			fi_fi: 'Asenna tämä web-sovellus laitteeseesi %device: paina %icon ja sen jälkeen valitse <strong>Lisää Koti-valikkoon</strong>.',
			fr_fr: 'Ajoutez cette application sur votre %device en cliquant sur %icon, puis <strong>Ajouter à l\'écran d\'accueil</strong>.',
			he_il: '<span dir="rtl">התקן אפליקציה זו על ה-%device שלך: הקש %icon ואז <strong>הוסף למסך הבית</strong>.</span>',
			hr_hr: 'Instaliraj ovu aplikaciju na svoj %device: klikni na %icon i odaberi <strong>Dodaj u početni zaslon</strong>.',
			hu_hu: 'Telepítse ezt a web-alkalmazást az Ön %device-jára: nyomjon a %icon-ra majd a <strong>Főképernyőhöz adás</strong> gombra.',
			it_it: 'Installa questa applicazione sul tuo %device: premi su %icon e poi <strong>Aggiungi a Home</strong>.',
			ja_jp: 'このウェブアプリをあなたの%deviceにインストールするには%iconをタップして<strong>ホーム画面に追加</strong>を選んでください。',
			ko_kr: '%device에 웹앱을 설치하려면 %icon을 터치 후 "홈화면에 추가"를 선택하세요',
			nb_no: 'Installer denne appen på din %device: trykk på %icon og deretter <strong>Legg til på Hjem-skjerm</strong>',
			nl_nl: 'Installeer deze webapp op uw %device: tik %icon en dan <strong>Voeg toe aan beginscherm</strong>.',
			pl_pl: 'Aby zainstalować tę aplikacje na %device: naciśnij %icon a następnie <strong>Dodaj jako ikonę</strong>.',
			pt_br: 'Instale este aplicativo em seu %device: aperte %icon e selecione <strong>Adicionar à Tela Inicio</strong>.',
			pt_pt: 'Para instalar esta aplicação no seu %device, prima o %icon e depois em <strong>Adicionar ao ecrã principal</strong>.',
			ru_ru: 'Установите это веб-приложение на ваш %device: нажмите %icon, затем <strong>Добавить в «Домой»</strong>.',
			sv_se: 'Lägg till denna webbapplikation på din %device: tryck på %icon och därefter <strong>Lägg till på hemskärmen</strong>.',
			th_th: 'ติดตั้งเว็บแอพฯ นี้บน %device ของคุณ: แตะ %icon และ <strong>เพิ่มที่หน้าจอโฮม</strong>',
			tr_tr: 'Bu uygulamayı %device\'a eklemek için %icon simgesine sonrasında <strong>Ana Ekrana Ekle</strong> düğmesine basın.',
			uk_ua: 'Встановіть цей веб сайт на Ваш %device: натисніть %icon, а потім <strong>На початковий екран</strong>.',
			zh_cn: '您可以将此应用程式安装到您的 %device 上。请按 %icon 然后点选<strong>添加至主屏幕</strong>。',
			zh_tw: '您可以將此應用程式安裝到您的 %device 上。請按 %icon 然後點選<strong>加入主畫面螢幕</strong>。'
		};

	function init () {
		// Preliminary check, all further checks are performed on iDevices only
		if ( !isIDevice ) return;

		var now = Date.now(),
			i;

		// Merge local with global options
		if ( w.addToHomeConfig ) {
			for ( i in w.addToHomeConfig ) {
				options[i] = w.addToHomeConfig[i];
			}
		}
		if ( !options.autostart ) options.hookOnLoad = false;

		isIPad = (/ipad/gi).test(nav.platform);
		isRetina = w.devicePixelRatio && w.devicePixelRatio > 1;
		isSafari = (/Safari/i).test(nav.appVersion) && !(/CriOS/i).test(nav.appVersion);
		isStandalone = nav.standalone;
		OSVersion = nav.appVersion.match(/OS (\d+_\d+)/i);
		OSVersion = OSVersion && OSVersion[1] ? +OSVersion[1].replace('_', '.') : 0;

		lastVisit = +w.localStorage.getItem('addToHome');

		isSessionActive = w.sessionStorage.getItem('addToHomeSession');
		isReturningVisitor = options.returningVisitor ? lastVisit && lastVisit + 28*24*60*60*1000 > now : true;

		if ( !lastVisit ) lastVisit = now;

		// If it is expired we need to reissue a new balloon
		isExpired = isReturningVisitor && lastVisit <= now;

		if ( options.hookOnLoad ) w.addEventListener('load', loaded, false);
		else if ( !options.hookOnLoad && options.autostart ) loaded();
	}

	function loaded () {
		w.removeEventListener('load', loaded, false);

		if ( !isReturningVisitor ) w.localStorage.setItem('addToHome', Date.now());
		else if ( options.expire && isExpired ) w.localStorage.setItem('addToHome', Date.now() + options.expire * 60000);

		if ( !overrideChecks && ( !isSafari || !isExpired || isSessionActive || isStandalone || !isReturningVisitor ) ) return;

		var touchIcon = '',
			platform = nav.platform.split(' ')[0],
			language = nav.language.replace('-', '_');

		balloon = document.createElement('div');
		balloon.id = 'addToHomeScreen';
		balloon.style.cssText += 'left:-9999px;-webkit-transition-property:-webkit-transform,opacity;-webkit-transition-duration:0;-webkit-transform:translate3d(0,0,0);position:' + (OSVersion < 5 ? 'absolute' : 'fixed');

		// Localize message
		if ( options.message in intl ) {		// You may force a language despite the user's locale
			language = options.message;
			options.message = '';
		}
		if ( options.message === '' ) {			// We look for a suitable language (defaulted to en_us)
			options.message = language in intl ? intl[language] : intl['en_us'];
		}

		if ( options.touchIcon ) {
			touchIcon = isRetina ?
				document.querySelector('head link[rel^=apple-touch-icon][sizes="114x114"],head link[rel^=apple-touch-icon][sizes="144x144"],head link[rel^=apple-touch-icon]') :
				document.querySelector('head link[rel^=apple-touch-icon][sizes="57x57"],head link[rel^=apple-touch-icon]');

			if ( touchIcon ) {
				touchIcon = '<span style="background-image:url(' + touchIcon.href + ')" class="addToHomeTouchIcon"></span>';
			}
		}

		balloon.className = (OSVersion >=7 ? 'addToHomeIOS7 ' : '') + (isIPad ? 'addToHomeIpad' : 'addToHomeIphone') + (touchIcon ? ' addToHomeWide' : '');
		balloon.innerHTML = touchIcon +
			options.message.replace('%device', platform).replace('%icon', OSVersion >= 4.2 ? '<span class="addToHomeShare"></span>' : '<span class="addToHomePlus">+</span>') +
			(options.arrow ? '<span class="addToHomeArrow"' + (OSVersion >= 7 && isIPad && touchIcon ? ' style="margin-left:-32px"' : '') + '></span>' : '') +
			(options.closeButton ? '<span class="addToHomeClose">\u00D7</span>' : '');

		document.body.appendChild(balloon);

		// Add the close action
		if ( options.closeButton ) balloon.addEventListener('click', clicked, false);

		if ( !isIPad && OSVersion >= 6 ) window.addEventListener('orientationchange', orientationCheck, false);

		setTimeout(show, options.startDelay);
	}

	function show () {
		var duration,
			iPadXShift = 208;

		// Set the initial position
		if ( isIPad ) {
			if ( OSVersion < 5 ) {
				startY = w.scrollY;
				startX = w.scrollX;
			} else if ( OSVersion < 6 ) {
				iPadXShift = 160;
			} else if ( OSVersion >= 7 ) {
				iPadXShift = 143;
			}

			balloon.style.top = startY + options.bottomOffset + 'px';
			balloon.style.left = Math.max(startX + iPadXShift - Math.round(balloon.offsetWidth / 2), 9) + 'px';

			switch ( options.animationIn ) {
				case 'drop':
					duration = '0.6s';
					balloon.style.webkitTransform = 'translate3d(0,' + -(w.scrollY + options.bottomOffset + balloon.offsetHeight) + 'px,0)';
					break;
				case 'bubble':
					duration = '0.6s';
					balloon.style.opacity = '0';
					balloon.style.webkitTransform = 'translate3d(0,' + (startY + 50) + 'px,0)';
					break;
				default:
					duration = '1s';
					balloon.style.opacity = '0';
			}
		} else {
			startY = w.innerHeight + w.scrollY;

			if ( OSVersion < 5 ) {
				startX = Math.round((w.innerWidth - balloon.offsetWidth) / 2) + w.scrollX;
				balloon.style.left = startX + 'px';
				balloon.style.top = startY - balloon.offsetHeight - options.bottomOffset + 'px';
			} else {
				balloon.style.left = '50%';
				balloon.style.marginLeft = -Math.round(balloon.offsetWidth / 2) - ( w.orientation%180 && OSVersion >= 6 && OSVersion < 7 ? 40 : 0 ) + 'px';
				balloon.style.bottom = options.bottomOffset + 'px';
			}

			switch (options.animationIn) {
				case 'drop':
					duration = '1s';
					balloon.style.webkitTransform = 'translate3d(0,' + -(startY + options.bottomOffset) + 'px,0)';
					break;
				case 'bubble':
					duration = '0.6s';
					balloon.style.webkitTransform = 'translate3d(0,' + (balloon.offsetHeight + options.bottomOffset + 50) + 'px,0)';
					break;
				default:
					duration = '1s';
					balloon.style.opacity = '0';
			}
		}

		balloon.offsetHeight;	// repaint trick
		balloon.style.webkitTransitionDuration = duration;
		balloon.style.opacity = '1';
		balloon.style.webkitTransform = 'translate3d(0,0,0)';
		balloon.addEventListener('webkitTransitionEnd', transitionEnd, false);

		closeTimeout = setTimeout(close, options.lifespan);
	}

	function manualShow (override) {
		if ( !isIDevice || balloon ) return;

		overrideChecks = override;
		loaded();
	}

	function close () {
		clearInterval( positionInterval );
		clearTimeout( closeTimeout );
		closeTimeout = null;

		// check if the popup is displayed and prevent errors
		if ( !balloon ) return;

		var posY = 0,
			posX = 0,
			opacity = '1',
			duration = '0';

		if ( options.closeButton ) balloon.removeEventListener('click', clicked, false);
		if ( !isIPad && OSVersion >= 6 ) window.removeEventListener('orientationchange', orientationCheck, false);

		if ( OSVersion < 5 ) {
			posY = isIPad ? w.scrollY - startY : w.scrollY + w.innerHeight - startY;
			posX = isIPad ? w.scrollX - startX : w.scrollX + Math.round((w.innerWidth - balloon.offsetWidth)/2) - startX;
		}

		balloon.style.webkitTransitionProperty = '-webkit-transform,opacity';

		switch ( options.animationOut ) {
			case 'drop':
				if ( isIPad ) {
					duration = '0.4s';
					opacity = '0';
					posY += 50;
				} else {
					duration = '0.6s';
					posY += balloon.offsetHeight + options.bottomOffset + 50;
				}
				break;
			case 'bubble':
				if ( isIPad ) {
					duration = '0.8s';
					posY -= balloon.offsetHeight + options.bottomOffset + 50;
				} else {
					duration = '0.4s';
					opacity = '0';
					posY -= 50;
				}
				break;
			default:
				duration = '0.8s';
				opacity = '0';
		}

		balloon.addEventListener('webkitTransitionEnd', transitionEnd, false);
		balloon.style.opacity = opacity;
		balloon.style.webkitTransitionDuration = duration;
		balloon.style.webkitTransform = 'translate3d(' + posX + 'px,' + posY + 'px,0)';
	}


	function clicked () {
		w.sessionStorage.setItem('addToHomeSession', '1');
		isSessionActive = true;
		close();
	}

	function transitionEnd () {
		balloon.removeEventListener('webkitTransitionEnd', transitionEnd, false);

		balloon.style.webkitTransitionProperty = '-webkit-transform';
		balloon.style.webkitTransitionDuration = '0.2s';

		// We reached the end!
		if ( !closeTimeout ) {
			balloon.parentNode.removeChild(balloon);
			balloon = null;
			return;
		}

		// On iOS 4 we start checking the element position
		if ( OSVersion < 5 && closeTimeout ) positionInterval = setInterval(setPosition, options.iterations);
	}

	function setPosition () {
		var matrix = new WebKitCSSMatrix(w.getComputedStyle(balloon, null).webkitTransform),
			posY = isIPad ? w.scrollY - startY : w.scrollY + w.innerHeight - startY,
			posX = isIPad ? w.scrollX - startX : w.scrollX + Math.round((w.innerWidth - balloon.offsetWidth) / 2) - startX;

		// Screen didn't move
		if ( posY == matrix.m42 && posX == matrix.m41 ) return;

		balloon.style.webkitTransform = 'translate3d(' + posX + 'px,' + posY + 'px,0)';
	}

	// Clear local and session storages (this is useful primarily in development)
	function reset () {
		w.localStorage.removeItem('addToHome');
		w.sessionStorage.removeItem('addToHomeSession');
	}

	function orientationCheck () {
		balloon.style.marginLeft = -Math.round(balloon.offsetWidth / 2) - ( w.orientation%180 && OSVersion >= 6 && OSVersion < 7 ? 40 : 0 ) + 'px';
	}

	// Bootstrap!
	init();

	return {
		show: manualShow,
		close: close,
		reset: reset
	};
})(window);
;
// i18next, v1.11.2
// Copyright (c)2015 Jan Mühlemann (jamuhl).
// Distributed under MIT license
// http://i18next.com
(function(root) {

    // add indexOf to non ECMA-262 standard compliant browsers
    if (!Array.prototype.indexOf) {
        Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
            "use strict";
            if (this == null) {
                throw new TypeError();
            }
            var t = Object(this);
            var len = t.length >>> 0;
            if (len === 0) {
                return -1;
            }
            var n = 0;
            if (arguments.length > 0) {
                n = Number(arguments[1]);
                if (n != n) { // shortcut for verifying if it's NaN
                    n = 0;
                } else if (n != 0 && n != Infinity && n != -Infinity) {
                    n = (n > 0 || -1) * Math.floor(Math.abs(n));
                }
            }
            if (n >= len) {
                return -1;
            }
            var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
            for (; k < len; k++) {
                if (k in t && t[k] === searchElement) {
                    return k;
                }
            }
            return -1;
        }
    }
    
    // add lastIndexOf to non ECMA-262 standard compliant browsers
    if (!Array.prototype.lastIndexOf) {
        Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) {
            "use strict";
            if (this == null) {
                throw new TypeError();
            }
            var t = Object(this);
            var len = t.length >>> 0;
            if (len === 0) {
                return -1;
            }
            var n = len;
            if (arguments.length > 1) {
                n = Number(arguments[1]);
                if (n != n) {
                    n = 0;
                } else if (n != 0 && n != (1 / 0) && n != -(1 / 0)) {
                    n = (n > 0 || -1) * Math.floor(Math.abs(n));
                }
            }
            var k = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);
            for (; k >= 0; k--) {
                if (k in t && t[k] === searchElement) {
                    return k;
                }
            }
            return -1;
        };
    }
    
    // Add string trim for IE8.
    if (typeof String.prototype.trim !== 'function') {
        String.prototype.trim = function() {
            return this.replace(/^\s+|\s+$/g, ''); 
        }
    }

    var $ = root.jQuery || root.Zepto
      , i18n = {}
      , resStore = {}
      , currentLng
      , replacementCounter = 0
      , languages = []
      , initialized = false
      , sync = {}
      , conflictReference = null;



    // Export the i18next object for **CommonJS**. 
    // If we're not in CommonJS, add `i18n` to the
    // global object or to jquery.
    if (typeof module !== 'undefined' && module.exports) {
        module.exports = i18n;
    } else {
        if ($) {
            $.i18n = $.i18n || i18n;
        }
        
        if (root.i18n) {
        	conflictReference = root.i18n;
        }
        root.i18n = i18n;
    }
    sync = {
    
        load: function(lngs, options, cb) {
            if (options.useLocalStorage) {
                sync._loadLocal(lngs, options, function(err, store) {
                    var missingLngs = [];
                    for (var i = 0, len = lngs.length; i < len; i++) {
                        if (!store[lngs[i]]) missingLngs.push(lngs[i]);
                    }
    
                    if (missingLngs.length > 0) {
                        sync._fetch(missingLngs, options, function(err, fetched) {
                            f.extend(store, fetched);
                            sync._storeLocal(fetched);
    
                            cb(err, store);
                        });
                    } else {
                        cb(err, store);
                    }
                });
            } else {
                sync._fetch(lngs, options, function(err, store){
                    cb(err, store);
                });
            }
        },
    
        _loadLocal: function(lngs, options, cb) {
            var store = {}
              , nowMS = new Date().getTime();
    
            if(window.localStorage) {
    
                var todo = lngs.length;
    
                f.each(lngs, function(key, lng) {
                    var local = f.localStorage.getItem('res_' + lng);
    
                    if (local) {
                        local = JSON.parse(local);
    
                        if (local.i18nStamp && local.i18nStamp + options.localStorageExpirationTime > nowMS) {
                            store[lng] = local;
                        }
                    }
    
                    todo--; // wait for all done befor callback
                    if (todo === 0) cb(null, store);
                });
            }
        },
    
        _storeLocal: function(store) {
            if(window.localStorage) {
                for (var m in store) {
                    store[m].i18nStamp = new Date().getTime();
                    f.localStorage.setItem('res_' + m, JSON.stringify(store[m]));
                }
            }
            return;
        },
    
        _fetch: function(lngs, options, cb) {
            var ns = options.ns
              , store = {};
            
            if (!options.dynamicLoad) {
                var todo = ns.namespaces.length * lngs.length
                  , errors;
    
                // load each file individual
                f.each(ns.namespaces, function(nsIndex, nsValue) {
                    f.each(lngs, function(lngIndex, lngValue) {
                        
                        // Call this once our translation has returned.
                        var loadComplete = function(err, data) {
                            if (err) {
                                errors = errors || [];
                                errors.push(err);
                            }
                            store[lngValue] = store[lngValue] || {};
                            store[lngValue][nsValue] = data;
    
                            todo--; // wait for all done befor callback
                            if (todo === 0) cb(errors, store);
                        };
                        
                        if(typeof options.customLoad == 'function'){
                            // Use the specified custom callback.
                            options.customLoad(lngValue, nsValue, options, loadComplete);
                        } else {
                            //~ // Use our inbuilt sync.
                            sync._fetchOne(lngValue, nsValue, options, loadComplete);
                        }
                    });
                });
            } else {
                // Call this once our translation has returned.
                var loadComplete = function(err, data) {
                    cb(err, data);
                };
    
                if(typeof options.customLoad == 'function'){
                    // Use the specified custom callback.
                    options.customLoad(lngs, ns.namespaces, options, loadComplete);
                } else {
                    var url = applyReplacement(options.resGetPath, { lng: lngs.join('+'), ns: ns.namespaces.join('+') });
                    // load all needed stuff once
                    f.ajax({
                        url: url,
                        cache: options.cache,
                        success: function(data, status, xhr) {
                            f.log('loaded: ' + url);
                            loadComplete(null, data);
                        },
                        error : function(xhr, status, error) {
                            f.log('failed loading: ' + url);
                            loadComplete('failed loading resource.json error: ' + error);
                        },
                        dataType: "json",
                        async : options.getAsync,
                        timeout: options.ajaxTimeout
                    });
                }    
            }
        },
    
        _fetchOne: function(lng, ns, options, done) {
            var url = applyReplacement(options.resGetPath, { lng: lng, ns: ns });
            f.ajax({
                url: url,
                cache: options.cache,
                success: function(data, status, xhr) {
                    f.log('loaded: ' + url);
                    done(null, data);
                },
                error : function(xhr, status, error) {
                    if ((status && status == 200) || (xhr && xhr.status && xhr.status == 200)) {
                        // file loaded but invalid json, stop waste time !
                        f.error('There is a typo in: ' + url);
                    } else if ((status && status == 404) || (xhr && xhr.status && xhr.status == 404)) {
                        f.log('Does not exist: ' + url);
                    } else {
                        var theStatus = status ? status : ((xhr && xhr.status) ? xhr.status : null);
                        f.log(theStatus + ' when loading ' + url);
                    }
                    
                    done(error, {});
                },
                dataType: "json",
                async : options.getAsync,
                timeout: options.ajaxTimeout,
                headers: options.headers
            });
        },
    
        postMissing: function(lng, ns, key, defaultValue, lngs) {
            var payload = {};
            payload[key] = defaultValue;
    
            var urls = [];
    
            if (o.sendMissingTo === 'fallback' && o.fallbackLng[0] !== false) {
                for (var i = 0; i < o.fallbackLng.length; i++) {
                    urls.push({lng: o.fallbackLng[i], url: applyReplacement(o.resPostPath, { lng: o.fallbackLng[i], ns: ns })});
                }
            } else if (o.sendMissingTo === 'current' || (o.sendMissingTo === 'fallback' && o.fallbackLng[0] === false) ) {
                urls.push({lng: lng, url: applyReplacement(o.resPostPath, { lng: lng, ns: ns })});
            } else if (o.sendMissingTo === 'all') {
                for (var i = 0, l = lngs.length; i < l; i++) {
                    urls.push({lng: lngs[i], url: applyReplacement(o.resPostPath, { lng: lngs[i], ns: ns })});
                }
            }
    
            for (var y = 0, len = urls.length; y < len; y++) {
                var item = urls[y];
                f.ajax({
                    url: item.url,
                    type: o.sendType,
                    data: payload,
                    success: function(data, status, xhr) {
                        f.log('posted missing key \'' + key + '\' to: ' + item.url);
    
                        // add key to resStore
                        var keys = key.split('.');
                        var x = 0;
                        var value = resStore[item.lng][ns];
                        while (keys[x]) {
                            if (x === keys.length - 1) {
                                value = value[keys[x]] = defaultValue;
                            } else {
                                value = value[keys[x]] = value[keys[x]] || {};
                            }
                            x++;
                        }
                    },
                    error : function(xhr, status, error) {
                        f.log('failed posting missing key \'' + key + '\' to: ' + item.url);
                    },
                    dataType: "json",
                    async : o.postAsync,
                    timeout: o.ajaxTimeout
                });
            }
        },
    
        reload: reload
    };
    // defaults
    var o = {
        lng: undefined,
        load: 'all',
        preload: [],
        lowerCaseLng: false,
        returnObjectTrees: false,
        fallbackLng: ['dev'],
        fallbackNS: [],
        detectLngQS: 'setLng',
        detectLngFromLocalStorage: false,
        ns: {
            namespaces: ['translation'],
            defaultNs: 'translation'
        },
        fallbackOnNull: true,
        fallbackOnEmpty: false,
        fallbackToDefaultNS: false,
        showKeyIfEmpty: false,
        nsseparator: ':',
        keyseparator: '.',
        selectorAttr: 'data-i18n',
        debug: false,
    
        resGetPath: 'locales/__lng__/__ns__.json',
        resPostPath: 'locales/add/__lng__/__ns__',
    
        getAsync: true,
        postAsync: true,
    
        resStore: undefined,
        useLocalStorage: false,
        localStorageExpirationTime: 7*24*60*60*1000,
    
        dynamicLoad: false,
        sendMissing: false,
        sendMissingTo: 'fallback', // current | all
        sendType: 'POST',
    
        interpolationPrefix: '__',
        interpolationSuffix: '__',
        defaultVariables: false,
        reusePrefix: '$t(',
        reuseSuffix: ')',
        pluralSuffix: '_plural',
        pluralNotFound: ['plural_not_found', Math.random()].join(''),
        contextNotFound: ['context_not_found', Math.random()].join(''),
        escapeInterpolation: false,
        indefiniteSuffix: '_indefinite',
        indefiniteNotFound: ['indefinite_not_found', Math.random()].join(''),
    
        setJqueryExt: true,
        defaultValueFromContent: true,
        useDataAttrOptions: false,
        cookieExpirationTime: undefined,
        useCookie: true,
        cookieName: 'i18next',
        cookieDomain: undefined,
    
        objectTreeKeyHandler: undefined,
        postProcess: undefined,
        parseMissingKey: undefined,
        missingKeyHandler: sync.postMissing,
        ajaxTimeout: 0,
    
        shortcutFunction: 'sprintf' // or: defaultValue
    };
    function _extend(target, source) {
        if (!source || typeof source === 'function') {
            return target;
        }
    
        for (var attr in source) { target[attr] = source[attr]; }
        return target;
    }
    
    function _deepExtend(target, source, overwrite) {
        for (var prop in source)
            if (prop in target) {
                // If we reached a leaf string in target or source then replace with source or skip depending on the 'overwrite' switch
                if (typeof target[prop] === 'string' || target[prop] instanceof String || typeof source[prop] === 'string' || source[prop] instanceof String) {
                    if (overwrite) {
                        target[prop] = source[prop];
                    }
                } else {
                    _deepExtend(target[prop], source[prop], overwrite);
                }
            } else {
                target[prop] = source[prop];
            }
        return target;
    }
    
    function _each(object, callback, args) {
        var name, i = 0,
            length = object.length,
            isObj = length === undefined || Object.prototype.toString.apply(object) !== '[object Array]' || typeof object === "function";
    
        if (args) {
            if (isObj) {
                for (name in object) {
                    if (callback.apply(object[name], args) === false) {
                        break;
                    }
                }
            } else {
                for ( ; i < length; ) {
                    if (callback.apply(object[i++], args) === false) {
                        break;
                    }
                }
            }
    
        // A special, fast, case for the most common use of each
        } else {
            if (isObj) {
                for (name in object) {
                    if (callback.call(object[name], name, object[name]) === false) {
                        break;
                    }
                }
            } else {
                for ( ; i < length; ) {
                    if (callback.call(object[i], i, object[i++]) === false) {
                        break;
                    }
                }
            }
        }
    
        return object;
    }
    
    var _entityMap = {
        "&": "&amp;",
        "<": "&lt;",
        ">": "&gt;",
        '"': '&quot;',
        "'": '&#39;',
        "/": '&#x2F;'
    };
    
    function _escape(data) {
        if (typeof data === 'string') {
            return data.replace(/[&<>"'\/]/g, function (s) {
                return _entityMap[s];
            });
        }else{
            return data;
        }
    }
    
    function _ajax(options) {
    
        // v0.5.0 of https://github.com/goloroden/http.js
        var getXhr = function (callback) {
            // Use the native XHR object if the browser supports it.
            if (window.XMLHttpRequest) {
                return callback(null, new XMLHttpRequest());
            } else if (window.ActiveXObject) {
                // In Internet Explorer check for ActiveX versions of the XHR object.
                try {
                    return callback(null, new ActiveXObject("Msxml2.XMLHTTP"));
                } catch (e) {
                    return callback(null, new ActiveXObject("Microsoft.XMLHTTP"));
                }
            }
    
            // If no XHR support was found, throw an error.
            return callback(new Error());
        };
    
        var encodeUsingUrlEncoding = function (data) {
            if(typeof data === 'string') {
                return data;
            }
    
            var result = [];
            for(var dataItem in data) {
                if(data.hasOwnProperty(dataItem)) {
                    result.push(encodeURIComponent(dataItem) + '=' + encodeURIComponent(data[dataItem]));
                }
            }
    
            return result.join('&');
        };
    
        var utf8 = function (text) {
            text = text.replace(/\r\n/g, '\n');
            var result = '';
    
            for(var i = 0; i < text.length; i++) {
                var c = text.charCodeAt(i);
    
                if(c < 128) {
                        result += String.fromCharCode(c);
                } else if((c > 127) && (c < 2048)) {
                        result += String.fromCharCode((c >> 6) | 192);
                        result += String.fromCharCode((c & 63) | 128);
                } else {
                        result += String.fromCharCode((c >> 12) | 224);
                        result += String.fromCharCode(((c >> 6) & 63) | 128);
                        result += String.fromCharCode((c & 63) | 128);
                }
            }
    
            return result;
        };
    
        var base64 = function (text) {
            var keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
    
            text = utf8(text);
            var result = '',
                    chr1, chr2, chr3,
                    enc1, enc2, enc3, enc4,
                    i = 0;
    
            do {
                chr1 = text.charCodeAt(i++);
                chr2 = text.charCodeAt(i++);
                chr3 = text.charCodeAt(i++);
    
                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;
    
                if(isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if(isNaN(chr3)) {
                    enc4 = 64;
                }
    
                result +=
                    keyStr.charAt(enc1) +
                    keyStr.charAt(enc2) +
                    keyStr.charAt(enc3) +
                    keyStr.charAt(enc4);
                chr1 = chr2 = chr3 = '';
                enc1 = enc2 = enc3 = enc4 = '';
            } while(i < text.length);
    
            return result;
        };
    
        var mergeHeaders = function () {
            // Use the first header object as base.
            var result = arguments[0];
    
            // Iterate through the remaining header objects and add them.
            for(var i = 1; i < arguments.length; i++) {
                var currentHeaders = arguments[i];
                for(var header in currentHeaders) {
                    if(currentHeaders.hasOwnProperty(header)) {
                        result[header] = currentHeaders[header];
                    }
                }
            }
    
            // Return the merged headers.
            return result;
        };
    
        var ajax = function (method, url, options, callback) {
            // Adjust parameters.
            if(typeof options === 'function') {
                callback = options;
                options = {};
            }
    
            // Set default parameter values.
            options.cache = options.cache || false;
            options.data = options.data || {};
            options.headers = options.headers || {};
            options.jsonp = options.jsonp || false;
            options.async = options.async === undefined ? true : options.async;
    
            // Merge the various header objects.
            var headers = mergeHeaders({
                'accept': '*/*',
                'content-type': 'application/x-www-form-urlencoded;charset=UTF-8'
            }, ajax.headers, options.headers);
    
            // Encode the data according to the content-type.
            var payload;
            if (headers['content-type'] === 'application/json') {
                payload = JSON.stringify(options.data);
            } else {
                payload = encodeUsingUrlEncoding(options.data);
            }
    
            // Specially prepare GET requests: Setup the query string, handle caching and make a JSONP call
            // if neccessary.
            if(method === 'GET') {
                // Setup the query string.
                var queryString = [];
                if(payload) {
                    queryString.push(payload);
                    payload = null;
                }
    
                // Handle caching.
                if(!options.cache) {
                    queryString.push('_=' + (new Date()).getTime());
                }
    
                // If neccessary prepare the query string for a JSONP call.
                if(options.jsonp) {
                    queryString.push('callback=' + options.jsonp);
                    queryString.push('jsonp=' + options.jsonp);
                }
    
                // Merge the query string and attach it to the url.
                queryString = queryString.join('&');
                if (queryString.length > 1) {
                    if (url.indexOf('?') > -1) {
                        url += '&' + queryString;
                    } else {
                        url += '?' + queryString;
                    }
                }
    
                // Make a JSONP call if neccessary.
                if(options.jsonp) {
                    var head = document.getElementsByTagName('head')[0];
                    var script = document.createElement('script');
                    script.type = 'text/javascript';
                    script.src = url;
                    head.appendChild(script);
                    return;
                }
            }
    
            // Since we got here, it is no JSONP request, so make a normal XHR request.
            getXhr(function (err, xhr) {
                if(err) return callback(err);
    
                // Open the request.
                xhr.open(method, url, options.async);
    
                // Set the request headers.
                for(var header in headers) {
                    if(headers.hasOwnProperty(header)) {
                        xhr.setRequestHeader(header, headers[header]);
                    }
                }
    
                // Handle the request events.
                xhr.onreadystatechange = function () {
                    if(xhr.readyState === 4) {
                        var data = xhr.responseText || '';
    
                        // If no callback is given, return.
                        if(!callback) {
                            return;
                        }
    
                        // Return an object that provides access to the data as text and JSON.
                        callback(xhr.status, {
                            text: function () {
                                return data;
                            },
    
                            json: function () {
                                try {
                                    return JSON.parse(data)
                                } catch (e) {
                                    f.error('Can not parse JSON. URL: ' + url);
                                    return {};
                                }
                            }
                        });
                    }
                };
    
                // Actually send the XHR request.
                xhr.send(payload);
            });
        };
    
        // Define the external interface.
        var http = {
            authBasic: function (username, password) {
                ajax.headers['Authorization'] = 'Basic ' + base64(username + ':' + password);
            },
    
            connect: function (url, options, callback) {
                return ajax('CONNECT', url, options, callback);
            },
    
            del: function (url, options, callback) {
                return ajax('DELETE', url, options, callback);
            },
    
            get: function (url, options, callback) {
                return ajax('GET', url, options, callback);
            },
    
            head: function (url, options, callback) {
                return ajax('HEAD', url, options, callback);
            },
    
            headers: function (headers) {
                ajax.headers = headers || {};
            },
    
            isAllowed: function (url, verb, callback) {
                this.options(url, function (status, data) {
                    callback(data.text().indexOf(verb) !== -1);
                });
            },
    
            options: function (url, options, callback) {
                return ajax('OPTIONS', url, options, callback);
            },
    
            patch: function (url, options, callback) {
                return ajax('PATCH', url, options, callback);
            },
    
            post: function (url, options, callback) {
                return ajax('POST', url, options, callback);
            },
    
            put: function (url, options, callback) {
                return ajax('PUT', url, options, callback);
            },
    
            trace: function (url, options, callback) {
                return ajax('TRACE', url, options, callback);
            }
        };
    
    
        var methode = options.type ? options.type.toLowerCase() : 'get';
    
        http[methode](options.url, options, function (status, data) {
            // file: protocol always gives status code 0, so check for data
            if (status === 200 || (status === 0 && data.text())) {
                options.success(data.json(), status, null);
            } else {
                options.error(data.text(), status, null);
            }
        });
    }
    
    var _cookie = {
        create: function(name,value,minutes,domain) {
            var expires;
            if (minutes) {
                var date = new Date();
                date.setTime(date.getTime()+(minutes*60*1000));
                expires = "; expires="+date.toGMTString();
            }
            else expires = "";
            domain = (domain)? "domain="+domain+";" : "";
            document.cookie = name+"="+value+expires+";"+domain+"path=/";
        },
    
        read: function(name) {
            var nameEQ = name + "=";
            var ca = document.cookie.split(';');
            for(var i=0;i < ca.length;i++) {
                var c = ca[i];
                while (c.charAt(0)==' ') c = c.substring(1,c.length);
                if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
            }
            return null;
        },
    
        remove: function(name) {
            this.create(name,"",-1);
        }
    };
    
    var cookie_noop = {
        create: function(name,value,minutes,domain) {},
        read: function(name) { return null; },
        remove: function(name) {}
    };
    
    
    
    // move dependent functions to a container so that
    // they can be overriden easier in no jquery environment (node.js)
    var f = {
        extend: $ ? $.extend : _extend,
        deepExtend: _deepExtend,
        each: $ ? $.each : _each,
        ajax: $ ? $.ajax : (typeof document !== 'undefined' ? _ajax : function() {}),
        cookie: typeof document !== 'undefined' ? _cookie : cookie_noop,
        detectLanguage: detectLanguage,
        escape: _escape,
        log: function(str) {
            if (o.debug && typeof console !== "undefined") console.log(str);
        },
        error: function(str) {
            if (typeof console !== "undefined") console.error(str);
        },
        getCountyIndexOfLng: function(lng) {
            var lng_index = 0;
            if (lng === 'nb-NO' || lng === 'nn-NO' || lng === 'nb-no' || lng === 'nn-no') lng_index = 1;
            return lng_index;
        },
        toLanguages: function(lng, fallbackLng) {
            var log = this.log;
    
            fallbackLng = fallbackLng || o.fallbackLng;
            if (typeof fallbackLng === 'string')
                fallbackLng = [fallbackLng];
    
            function applyCase(l) {
                var ret = l;
    
                if (typeof l === 'string' && l.indexOf('-') > -1) {
                    var parts = l.split('-');
    
                    ret = o.lowerCaseLng ?
                        parts[0].toLowerCase() +  '-' + parts[1].toLowerCase() :
                        parts[0].toLowerCase() +  '-' + parts[1].toUpperCase();
                } else {
                    ret = o.lowerCaseLng ? l.toLowerCase() : l;
                }
    
                return ret;
            }
    
            var languages = [];
            var whitelist = o.lngWhitelist || false;
            var addLanguage = function(language){
              //reject langs not whitelisted
              if(!whitelist || whitelist.indexOf(language) > -1){
                languages.push(language);
              }else{
                log('rejecting non-whitelisted language: ' + language);
              }
            };
            if (typeof lng === 'string' && lng.indexOf('-') > -1) {
                var parts = lng.split('-');
    
                if (o.load !== 'unspecific') addLanguage(applyCase(lng));
                if (o.load !== 'current') addLanguage(applyCase(parts[this.getCountyIndexOfLng(lng)]));
            } else {
                addLanguage(applyCase(lng));
            }
    
            for (var i = 0; i < fallbackLng.length; i++) {
                if (languages.indexOf(fallbackLng[i]) === -1 && fallbackLng[i]) languages.push(applyCase(fallbackLng[i]));
            }
            return languages;
        },
        regexEscape: function(str) {
            return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
        },
        regexReplacementEscape: function(strOrFn) {
            if (typeof strOrFn === 'string') {
                return strOrFn.replace(/\$/g, "$$$$");
            } else {
                return strOrFn;
            }
        },
        localStorage: {
            setItem: function(key, value) {
                if (window.localStorage) {
                    try {
                        window.localStorage.setItem(key, value);
                    } catch (e) {
                        f.log('failed to set value for key "' + key + '" to localStorage.');
                    }
                }
            },
            getItem: function(key, value) {
                if (window.localStorage) {
                    try {
                        return window.localStorage.getItem(key, value);
                    } catch (e) {
                        f.log('failed to get value for key "' + key + '" from localStorage.');
                        return undefined;
                    }
                }
            }
        }
    };
    function init(options, cb) {
    
        if (typeof options === 'function') {
            cb = options;
            options = {};
        }
        options = options || {};
    
        // override defaults with passed in options
        f.extend(o, options);
        delete o.fixLng; /* passed in each time */
    
        // override functions: .log(), .detectLanguage(), etc
        if (o.functions) {
            delete o.functions;
            f.extend(f, options.functions);
        }
    
        // create namespace object if namespace is passed in as string
        if (typeof o.ns == 'string') {
            o.ns = { namespaces: [o.ns], defaultNs: o.ns};
        }
    
        // fallback namespaces
        if (typeof o.fallbackNS == 'string') {
            o.fallbackNS = [o.fallbackNS];
        }
    
        // fallback languages
        if (typeof o.fallbackLng == 'string' || typeof o.fallbackLng == 'boolean') {
            o.fallbackLng = [o.fallbackLng];
        }
    
        // escape prefix/suffix
        o.interpolationPrefixEscaped = f.regexEscape(o.interpolationPrefix);
        o.interpolationSuffixEscaped = f.regexEscape(o.interpolationSuffix);
    
        if (!o.lng) o.lng = f.detectLanguage();
    
        languages = f.toLanguages(o.lng);
        currentLng = languages[0];
        f.log('currentLng set to: ' + currentLng);
    
        if (o.useCookie && f.cookie.read(o.cookieName) !== currentLng){ //cookie is unset or invalid
            f.cookie.create(o.cookieName, currentLng, o.cookieExpirationTime, o.cookieDomain);
        }
        if (o.detectLngFromLocalStorage && typeof document !== 'undefined' && window.localStorage) {
            f.localStorage.setItem('i18next_lng', currentLng);
        }
    
        var lngTranslate = translate;
        if (options.fixLng) {
            lngTranslate = function(key, options) {
                options = options || {};
                options.lng = options.lng || lngTranslate.lng;
                return translate(key, options);
            };
            lngTranslate.lng = currentLng;
        }
    
        pluralExtensions.setCurrentLng(currentLng);
    
        // add JQuery extensions
        if ($ && o.setJqueryExt) {
            addJqueryFunct && addJqueryFunct();
        } else {
           addJqueryLikeFunctionality && addJqueryLikeFunctionality();
        }
    
        // jQuery deferred
        var deferred;
        if ($ && $.Deferred) {
            deferred = $.Deferred();
        }
    
        // return immidiatly if res are passed in
        if (o.resStore) {
            resStore = o.resStore;
            initialized = true;
            if (cb) cb(null, lngTranslate);
            if (deferred) deferred.resolve(lngTranslate);
            if (deferred) return deferred.promise();
            return;
        }
    
        // languages to load
        var lngsToLoad = f.toLanguages(o.lng);
        if (typeof o.preload === 'string') o.preload = [o.preload];
        for (var i = 0, l = o.preload.length; i < l; i++) {
            var pres = f.toLanguages(o.preload[i]);
            for (var y = 0, len = pres.length; y < len; y++) {
                if (lngsToLoad.indexOf(pres[y]) < 0) {
                    lngsToLoad.push(pres[y]);
                }
            }
        }
    
        // else load them
        i18n.sync.load(lngsToLoad, o, function(err, store) {
            resStore = store;
            initialized = true;
    
            if (cb) cb(err, lngTranslate);
            if (deferred) (!err ? deferred.resolve : deferred.reject)(err || lngTranslate);
        });
    
        if (deferred) return deferred.promise();
    }
    
    function isInitialized() {
        return initialized;
    }
    function preload(lngs, cb) {
        if (typeof lngs === 'string') lngs = [lngs];
        for (var i = 0, l = lngs.length; i < l; i++) {
            if (o.preload.indexOf(lngs[i]) < 0) {
                o.preload.push(lngs[i]);
            }
        }
        return init(cb);
    }
    
    function addResourceBundle(lng, ns, resources, deep, overwrite) {
        if (typeof ns !== 'string') {
            resources = ns;
            ns = o.ns.defaultNs;
        } else if (o.ns.namespaces.indexOf(ns) < 0) {
            o.ns.namespaces.push(ns);
        }
    
        resStore[lng] = resStore[lng] || {};
        resStore[lng][ns] = resStore[lng][ns] || {};
    
        if (deep) {
            f.deepExtend(resStore[lng][ns], resources, overwrite);
        } else {
            f.extend(resStore[lng][ns], resources);
        }
        if (o.useLocalStorage) {
            sync._storeLocal(resStore);
        }
    }
    
    function hasResourceBundle(lng, ns) {
        if (typeof ns !== 'string') {
            ns = o.ns.defaultNs;
        }
    
        resStore[lng] = resStore[lng] || {};
        var res = resStore[lng][ns] || {};
    
        var hasValues = false;
        for(var prop in res) {
            if (res.hasOwnProperty(prop)) {
                hasValues = true;
            }
        }
    
        return hasValues;
    }
    
    function getResourceBundle(lng, ns) {
        if (typeof ns !== 'string') {
            ns = o.ns.defaultNs;
        }
    
        resStore[lng] = resStore[lng] || {};
        return f.extend({}, resStore[lng][ns]);
    }
    
    function removeResourceBundle(lng, ns) {
        if (typeof ns !== 'string') {
            ns = o.ns.defaultNs;
        }
    
        resStore[lng] = resStore[lng] || {};
        resStore[lng][ns] = {};
        if (o.useLocalStorage) {
            sync._storeLocal(resStore);
        }
    }
    
    function addResource(lng, ns, key, value) {
        if (typeof ns !== 'string') {
            resource = ns;
            ns = o.ns.defaultNs;
        } else if (o.ns.namespaces.indexOf(ns) < 0) {
            o.ns.namespaces.push(ns);
        }
    
        resStore[lng] = resStore[lng] || {};
        resStore[lng][ns] = resStore[lng][ns] || {};
    
        var keys = key.split(o.keyseparator);
        var x = 0;
        var node = resStore[lng][ns];
        var origRef = node;
    
        while (keys[x]) {
            if (x == keys.length - 1)
                node[keys[x]] = value;
            else {
                if (node[keys[x]] == null)
                    node[keys[x]] = {};
    
                node = node[keys[x]];
            }
            x++;
        }
        if (o.useLocalStorage) {
            sync._storeLocal(resStore);
        }
    }
    
    function addResources(lng, ns, resources) {
        if (typeof ns !== 'string') {
            resources = ns;
            ns = o.ns.defaultNs;
        } else if (o.ns.namespaces.indexOf(ns) < 0) {
            o.ns.namespaces.push(ns);
        }
    
        for (var m in resources) {
            if (typeof resources[m] === 'string') addResource(lng, ns, m, resources[m]);
        }
    }
    
    function setDefaultNamespace(ns) {
        o.ns.defaultNs = ns;
    }
    
    function loadNamespace(namespace, cb) {
        loadNamespaces([namespace], cb);
    }
    
    function loadNamespaces(namespaces, cb) {
        var opts = {
            dynamicLoad: o.dynamicLoad,
            resGetPath: o.resGetPath,
            getAsync: o.getAsync,
            customLoad: o.customLoad,
            ns: { namespaces: namespaces, defaultNs: ''} /* new namespaces to load */
        };
    
        // languages to load
        var lngsToLoad = f.toLanguages(o.lng);
        if (typeof o.preload === 'string') o.preload = [o.preload];
        for (var i = 0, l = o.preload.length; i < l; i++) {
            var pres = f.toLanguages(o.preload[i]);
            for (var y = 0, len = pres.length; y < len; y++) {
                if (lngsToLoad.indexOf(pres[y]) < 0) {
                    lngsToLoad.push(pres[y]);
                }
            }
        }
    
        // check if we have to load
        var lngNeedLoad = [];
        for (var a = 0, lenA = lngsToLoad.length; a < lenA; a++) {
            var needLoad = false;
            var resSet = resStore[lngsToLoad[a]];
            if (resSet) {
                for (var b = 0, lenB = namespaces.length; b < lenB; b++) {
                    if (!resSet[namespaces[b]]) needLoad = true;
                }
            } else {
                needLoad = true;
            }
    
            if (needLoad) lngNeedLoad.push(lngsToLoad[a]);
        }
    
        if (lngNeedLoad.length) {
            i18n.sync._fetch(lngNeedLoad, opts, function(err, store) {
                var todo = namespaces.length * lngNeedLoad.length;
    
                // load each file individual
                f.each(namespaces, function(nsIndex, nsValue) {
    
                    // append namespace to namespace array
                    if (o.ns.namespaces.indexOf(nsValue) < 0) {
                        o.ns.namespaces.push(nsValue);
                    }
    
                    f.each(lngNeedLoad, function(lngIndex, lngValue) {
                        resStore[lngValue] = resStore[lngValue] || {};
                        resStore[lngValue][nsValue] = store[lngValue][nsValue];
    
                        todo--; // wait for all done befor callback
                        if (todo === 0 && cb) {
                            if (o.useLocalStorage) i18n.sync._storeLocal(resStore);
                            cb();
                        }
                    });
                });
            });
        } else {
            if (cb) cb();
        }
    }
    
    function setLng(lng, options, cb) {
        if (typeof options === 'function') {
            cb = options;
            options = {};
        } else if (!options) {
            options = {};
        }
    
        options.lng = lng;
        return init(options, cb);
    }
    
    function lng() {
        return currentLng;
    }
    
    function dir() {   
        var rtlLangs = [ "ar", "shu", "sqr", "ssh", "xaa", "yhd", "yud", "aao", "abh", "abv", "acm",
            "acq", "acw", "acx", "acy", "adf", "ads", "aeb", "aec", "afb", "ajp", "apc", "apd", "arb",
            "arq", "ars", "ary", "arz", "auz", "avl", "ayh", "ayl", "ayn", "ayp", "bbz", "pga", "he",
            "iw", "ps", "pbt", "pbu", "pst", "prp", "prd", "ur", "ydd", "yds", "yih", "ji", "yi", "hbo",
            "men", "xmn", "fa", "jpr", "peo", "pes", "prs", "dv", "sam"
        ];
    
        if ( rtlLangs.some( function( lang ) {
                return new RegExp( '^' + lang ).test( currentLng );
            } ) ) {
            return 'rtl';
        }
        return 'ltr';
    }
    
    function reload(cb) {
        resStore = {};
        setLng(currentLng, cb);
    }
    
    function noConflict() {
        
        window.i18next = window.i18n;
    
        if (conflictReference) {
            window.i18n = conflictReference;
        } else {
            delete window.i18n;
        }
    }
    function addJqueryFunct() {
        // $.t shortcut
        $.t = $.t || translate;
    
        function parse(ele, key, options) {
            if (key.length === 0) return;
    
            var attr = 'text';
    
            if (key.indexOf('[') === 0) {
                var parts = key.split(']');
                key = parts[1];
                attr = parts[0].substr(1, parts[0].length-1);
            }
    
            if (key.indexOf(';') === key.length-1) {
                key = key.substr(0, key.length-2);
            }
    
            var optionsToUse;
            if (attr === 'html') {
                optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
                ele.html($.t(key, optionsToUse));
            } else if (attr === 'text') {
                optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.text() }, options) : options;
                ele.text($.t(key, optionsToUse));
            } else if (attr === 'prepend') {
                optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
                ele.prepend($.t(key, optionsToUse));
            } else if (attr === 'append') {
                optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.html() }, options) : options;
                ele.append($.t(key, optionsToUse));
            } else if (attr.indexOf("data-") === 0) {
                var dataAttr = attr.substr(("data-").length);
                optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.data(dataAttr) }, options) : options;
                var translated = $.t(key, optionsToUse);
                //we change into the data cache
                ele.data(dataAttr, translated);
                //we change into the dom
                ele.attr(attr, translated);
            } else {
                optionsToUse = o.defaultValueFromContent ? $.extend({ defaultValue: ele.attr(attr) }, options) : options;
                ele.attr(attr, $.t(key, optionsToUse));
            }
        }
    
        function localize(ele, options) {
            var key = ele.attr(o.selectorAttr);
            if (!key && typeof key !== 'undefined' && key !== false) key = ele.text() || ele.val();
            if (!key) return;
    
            var target = ele
              , targetSelector = ele.data("i18n-target");
            if (targetSelector) {
                target = ele.find(targetSelector) || ele;
            }
    
            if (!options && o.useDataAttrOptions === true) {
                options = ele.data("i18n-options");
            }
            options = options || {};
    
            if (key.indexOf(';') >= 0) {
                var keys = key.split(';');
    
                $.each(keys, function(m, k) {
                    if (k !== '') parse(target, k, options);
                });
    
            } else {
                parse(target, key, options);
            }
    
            if (o.useDataAttrOptions === true) {
              var clone = $.extend({lng: 'non', lngs: [], _origLng: 'non'}, options);
              delete clone.lng;
              delete clone.lngs;
              delete clone._origLng;
              ele.data("i18n-options", clone);
            }
        }
    
        // fn
        $.fn.i18n = function (options) {
            return this.each(function() {
                // localize element itself
                localize($(this), options);
    
                // localize childs
                var elements =  $(this).find('[' + o.selectorAttr + ']');
                elements.each(function() {
                    localize($(this), options);
                });
            });
        };
    }
    function addJqueryLikeFunctionality() {
    
        function parse(ele, key, options) {
            if (key.length === 0) return;
    
            var attr = 'text';
    
            if (key.indexOf('[') === 0) {
                var parts = key.split(']');
                key = parts[1];
                attr = parts[0].substr(1, parts[0].length-1);
            }
    
            if (key.indexOf(';') === key.length-1) {
                key = key.substr(0, key.length-2);
            }
    
            if (attr === 'html') {
                ele.innerHTML = translate(key, options);
            } else if (attr === 'text') {
                ele.textContent = translate(key, options);
            } else if (attr === 'prepend') {
                ele.insertAdjacentHTML(translate(key, options), 'afterbegin');
            } else if (attr === 'append') {
                ele.insertAdjacentHTML(translate(key, options), 'beforeend');
            } else {
                ele.setAttribute(attr, translate(key, options));
            }
        }
    
        function localize(ele, options) {
            var key = ele.getAttribute(o.selectorAttr);
            if (!key && typeof key !== 'undefined' && key !== false) key = ele.textContent || ele.value;
            if (!key) return;
    
            var target = ele
              , targetSelector = ele.getAttribute("i18n-target");
            if (targetSelector) {
                target = ele.querySelector(targetSelector) || ele;
            }
            
            if (key.indexOf(';') >= 0) {
                var keys = key.split(';'), index = 0, length = keys.length;
                
                for ( ; index < length; index++) {
                    if (keys[index] !== '') parse(target, keys[index], options);
                }
    
            } else {
                parse(target, key, options);
            }
        }
    
        // fn
        i18n.translateObject = function (object, options) {
            // localize childs
            var elements =  object.querySelectorAll('[' + o.selectorAttr + ']');
            var index = 0, length = elements.length;
            for ( ; index < length; index++) {
                localize(elements[index], options);
            }
        };
    }
    function applyReplacement(str, replacementHash, nestedKey, options) {
        if (!str) return str;
    
        options = options || replacementHash; // first call uses replacement hash combined with options
        if (str.indexOf(options.interpolationPrefix || o.interpolationPrefix) < 0) return str;
    
        var prefix = options.interpolationPrefix ? f.regexEscape(options.interpolationPrefix) : o.interpolationPrefixEscaped
          , suffix = options.interpolationSuffix ? f.regexEscape(options.interpolationSuffix) : o.interpolationSuffixEscaped
          , keyseparator = options.keyseparator || o.keyseparator
          , unEscapingSuffix = 'HTML'+suffix;
    
        var hash = replacementHash.replace && typeof replacementHash.replace === 'object' ? replacementHash.replace : replacementHash;
        var replacementRegex = new RegExp([prefix, '(.+?)', '(HTML)?', suffix].join(''), 'g');
        var escapeInterpolation = options.escapeInterpolation || o.escapeInterpolation;
        return str.replace(replacementRegex, function (wholeMatch, keyMatch, htmlMatched) {
            // Check for recursive matches of object
            var objectMatching = hash;
            var keyLeaf = keyMatch;
            while (keyLeaf.indexOf(keyseparator) >= 0 && typeof objectMatching === 'object' && objectMatching) {
                var propName = keyLeaf.slice(0, keyLeaf.indexOf(keyseparator));
                keyLeaf = keyLeaf.slice(keyLeaf.indexOf(keyseparator) + 1);
                objectMatching = objectMatching[propName];
            }
            if (objectMatching && typeof objectMatching === 'object' && objectMatching.hasOwnProperty(keyLeaf)) {
                    var value = objectMatching[keyLeaf];
                if (escapeInterpolation && !htmlMatched) {
                    return f.escape(objectMatching[keyLeaf]);
                } else {
                    return objectMatching[keyLeaf];
                }
            } else {
                return wholeMatch;
            }
        });
    }
    
    // append it to functions
    f.applyReplacement = applyReplacement;
    
    function applyReuse(translated, options) {
        var comma = ',';
        var options_open = '{';
        var options_close = '}';
    
        var opts = f.extend({}, options);
        delete opts.postProcess;
        delete opts.isFallbackLookup;
    
        while (translated.indexOf(o.reusePrefix) != -1) {
            replacementCounter++;
            if (replacementCounter > o.maxRecursion) { break; } // safety net for too much recursion
            var index_of_opening = translated.lastIndexOf(o.reusePrefix);
            var index_of_end_of_closing = translated.indexOf(o.reuseSuffix, index_of_opening) + o.reuseSuffix.length;
            var token = translated.substring(index_of_opening, index_of_end_of_closing);
            var token_without_symbols = token.replace(o.reusePrefix, '').replace(o.reuseSuffix, '');
    
            if (index_of_end_of_closing <= index_of_opening) {
                f.error('there is an missing closing in following translation value', translated);
                return '';
            }
    
            if (token_without_symbols.indexOf(comma) != -1) {
                var index_of_token_end_of_closing = token_without_symbols.indexOf(comma);
                if (token_without_symbols.indexOf(options_open, index_of_token_end_of_closing) != -1 && token_without_symbols.indexOf(options_close, index_of_token_end_of_closing) != -1) {
                    var index_of_opts_opening = token_without_symbols.indexOf(options_open, index_of_token_end_of_closing);
                    var index_of_opts_end_of_closing = token_without_symbols.indexOf(options_close, index_of_opts_opening) + options_close.length;
                    try {
                        opts = f.extend(opts, JSON.parse(token_without_symbols.substring(index_of_opts_opening, index_of_opts_end_of_closing)));
                        token_without_symbols = token_without_symbols.substring(0, index_of_token_end_of_closing);
                    } catch (e) {
                    }
                }
            }
    
            var translated_token = _translate(token_without_symbols, opts);
            translated = translated.replace(token, f.regexReplacementEscape(translated_token));
        }
        return translated;
    }
    
    function hasContext(options) {
        return (options.context && (typeof options.context == 'string' || typeof options.context == 'number'));
    }
    
    function needsPlural(options, lng) {
        return (options.count !== undefined && typeof options.count != 'string'/* && pluralExtensions.needsPlural(lng, options.count)*/);
    }
    
    function needsIndefiniteArticle(options) {
        return (options.indefinite_article !== undefined && typeof options.indefinite_article != 'string' && options.indefinite_article);
    }
    
    function exists(key, options) {
        options = options || {};
    
        var notFound = _getDefaultValue(key, options)
            , found = _find(key, options);
    
        return found !== undefined || found === notFound;
    }
    
    function translate(key, options) {
        if (!initialized) {
            f.log('i18next not finished initialization. you might have called t function before loading resources finished.')
    
            if (options && options.defaultValue) {
                return options.detaultValue;
            } else {
                return '';
            }
        };
        replacementCounter = 0;
        return _translate.apply(null, arguments);
    }
    
    function _getDefaultValue(key, options) {
        return (options.defaultValue !== undefined) ? options.defaultValue : key;
    }
    
    function _injectSprintfProcessor() {
    
        var values = [];
    
        // mh: build array from second argument onwards
        for (var i = 1; i < arguments.length; i++) {
            values.push(arguments[i]);
        }
    
        return {
            postProcess: 'sprintf',
            sprintf:     values
        };
    }
    
    function _translate(potentialKeys, options) {
        if (typeof options !== 'undefined' && typeof options !== 'object') {
            if (o.shortcutFunction === 'sprintf') {
                // mh: gettext like sprintf syntax found, automatically create sprintf processor
                options = _injectSprintfProcessor.apply(null, arguments);
            } else if (o.shortcutFunction === 'defaultValue') {
                options = {
                    defaultValue: options
                }
            }
        } else {
            options = options || {};
        }
    
        if (typeof o.defaultVariables === 'object') {
            options = f.extend({}, o.defaultVariables, options);
        }
    
        if (potentialKeys === undefined || potentialKeys === null || potentialKeys === '') return '';
    
        if (typeof potentialKeys === 'number') {
            potentialKeys = String(potentialKeys);
        }
    
        if (typeof potentialKeys === 'string') {
            potentialKeys = [potentialKeys];
        }
    
        var key = potentialKeys[0];
    
        if (potentialKeys.length > 1) {
            for (var i = 0; i < potentialKeys.length; i++) {
                key = potentialKeys[i];
                if (exists(key, options)) {
                    break;
                }
            }
        }
    
        var notFound = _getDefaultValue(key, options)
            , found = _find(key, options)
            , nsseparator = options.nsseparator || o.nsseparator
            , lngs = options.lng ? f.toLanguages(options.lng, options.fallbackLng) : languages
            , ns = options.ns || o.ns.defaultNs
            , parts;
    
        // split ns and key
        if (key.indexOf(nsseparator) > -1) {
            parts = key.split(nsseparator);
            ns = parts[0];
            key = parts[1];
        }
    
        if (found === undefined && o.sendMissing && typeof o.missingKeyHandler === 'function') {
            if (options.lng) {
                o.missingKeyHandler(lngs[0], ns, key, notFound, lngs);
            } else {
                o.missingKeyHandler(o.lng, ns, key, notFound, lngs);
            }
        }
    
        var postProcessorsToApply,
            postProcessor,
            j;
        if (typeof o.postProcess === 'string' && o.postProcess !== '') {
            postProcessorsToApply = [o.postProcess];
        } else if (typeof o.postProcess === 'array' || typeof o.postProcess === 'object') {
            postProcessorsToApply = o.postProcess;
        } else {
            postProcessorsToApply = [];
        }
    
        if (typeof options.postProcess === 'string' && options.postProcess !== '') {
            postProcessorsToApply = postProcessorsToApply.concat([options.postProcess]);
        } else if (typeof options.postProcess === 'array' || typeof options.postProcess === 'object') {
            postProcessorsToApply = postProcessorsToApply.concat(options.postProcess);
        }
    
        if (found !== undefined && postProcessorsToApply.length) {
            for (j = 0; j < postProcessorsToApply.length; j += 1) {
                postProcessor = postProcessorsToApply[j];
                if (postProcessors[postProcessor]) {
                    found = postProcessors[postProcessor](found, key, options);
                }
            }
        }
    
        // process notFound if function exists
        var splitNotFound = notFound;
        if (notFound.indexOf(nsseparator) > -1) {
            parts = notFound.split(nsseparator);
            splitNotFound = parts[1];
        }
        if (splitNotFound === key && o.parseMissingKey) {
            notFound = o.parseMissingKey(notFound);
        }
    
        if (found === undefined) {
            notFound = applyReplacement(notFound, options);
            notFound = applyReuse(notFound, options);
    
            if (postProcessorsToApply.length) {
                found = _getDefaultValue(key, options);
                for (j = 0; j < postProcessorsToApply.length; j += 1) {
                    postProcessor = postProcessorsToApply[j];
                    if (postProcessors[postProcessor]) {
                        found = postProcessors[postProcessor](found, key, options);
                    }
                }
            }
        }
    
        return (found !== undefined) ? found : notFound;
    }
    
    function _find(key, options) {
        options = options || {};
    
        var optionWithoutCount, translated
            , notFound = _getDefaultValue(key, options)
            , lngs = languages;
    
        if (!resStore) { return notFound; } // no resStore to translate from
    
        // CI mode
        if (lngs[0].toLowerCase() === 'cimode') return notFound;
    
        // passed in lng
        if (options.lngs) lngs = options.lngs;
        if (options.lng) {
            lngs = f.toLanguages(options.lng, options.fallbackLng);
    
            if (!resStore[lngs[0]]) {
                var oldAsync = o.getAsync;
                o.getAsync = false;
    
                i18n.sync.load(lngs, o, function(err, store) {
                    f.extend(resStore, store);
                    o.getAsync = oldAsync;
                });
            }
        }
    
        var ns = options.ns || o.ns.defaultNs;
        var nsseparator = options.nsseparator || o.nsseparator;
        if (key.indexOf(nsseparator) > -1) {
            var parts = key.split(nsseparator);
            ns = parts[0];
            key = parts[1];
        }
    
        if (hasContext(options)) {
            optionWithoutCount = f.extend({}, options);
            delete optionWithoutCount.context;
            optionWithoutCount.defaultValue = o.contextNotFound;
    
            var contextKey = ns + nsseparator + key + '_' + options.context;
    
            translated = translate(contextKey, optionWithoutCount);
            if (translated != o.contextNotFound) {
                return applyReplacement(translated, { context: options.context }); // apply replacement for context only
            } // else continue translation with original/nonContext key
        }
    
        if (needsPlural(options, lngs[0])) {
            optionWithoutCount = f.extend({ lngs: [lngs[0]]}, options);
            delete optionWithoutCount.count;
            optionWithoutCount._origLng = optionWithoutCount._origLng || optionWithoutCount.lng || lngs[0];
            delete optionWithoutCount.lng;
            optionWithoutCount.defaultValue = o.pluralNotFound;
    
            var pluralKey;
            if (!pluralExtensions.needsPlural(lngs[0], options.count)) {
                pluralKey = ns + nsseparator + key;
            } else {
                pluralKey = ns + nsseparator + key + o.pluralSuffix;
                var pluralExtension = pluralExtensions.get(lngs[0], options.count);
                if (pluralExtension >= 0) {
                    pluralKey = pluralKey + '_' + pluralExtension;
                } else if (pluralExtension === 1) {
                    pluralKey = ns + nsseparator + key; // singular
                }
            }
    
            translated = translate(pluralKey, optionWithoutCount);
    
            if (translated != o.pluralNotFound) {
                return applyReplacement(translated, {
                    count: options.count,
                    interpolationPrefix: options.interpolationPrefix,
                    interpolationSuffix: options.interpolationSuffix
                }); // apply replacement for count only
            } else if (lngs.length > 1) {
                // remove failed lng
                var clone = lngs.slice();
                clone.shift();
                options = f.extend(options, { lngs: clone });
                options._origLng = optionWithoutCount._origLng;
                delete options.lng;
                // retry with fallbacks
                translated = translate(ns + nsseparator + key, options);
                if (translated != o.pluralNotFound) return translated;
            } else {
                optionWithoutCount.lng = optionWithoutCount._origLng;
                delete optionWithoutCount._origLng;
                translated = translate(ns + nsseparator + key, optionWithoutCount);
    
                return applyReplacement(translated, {
                    count: options.count,
                    interpolationPrefix: options.interpolationPrefix,
                    interpolationSuffix: options.interpolationSuffix
                });
            }
        }
    
        if (needsIndefiniteArticle(options)) {
            var optionsWithoutIndef = f.extend({}, options);
            delete optionsWithoutIndef.indefinite_article;
            optionsWithoutIndef.defaultValue = o.indefiniteNotFound;
            // If we don't have a count, we want the indefinite, if we do have a count, and needsPlural is false
            var indefiniteKey = ns + nsseparator + key + (((options.count && !needsPlural(options, lngs[0])) || !options.count) ? o.indefiniteSuffix : "");
            translated = translate(indefiniteKey, optionsWithoutIndef);
            if (translated != o.indefiniteNotFound) {
                return translated;
            }
        }
    
        var found;
        var keyseparator = options.keyseparator || o.keyseparator;
        var keys = key.split(keyseparator);
        for (var i = 0, len = lngs.length; i < len; i++ ) {
            if (found !== undefined) break;
    
            var l = lngs[i];
    
            var x = 0;
            var value = resStore[l] && resStore[l][ns];
            while (keys[x]) {
                value = value && value[keys[x]];
                x++;
            }
            if (value !== undefined && (!o.showKeyIfEmpty || value !== '')) {
                var valueType = Object.prototype.toString.apply(value);
                if (typeof value === 'string') {
                    value = applyReplacement(value, options);
                    value = applyReuse(value, options);
                } else if (valueType === '[object Array]' && !o.returnObjectTrees && !options.returnObjectTrees) {
                    value = value.join('\n');
                    value = applyReplacement(value, options);
                    value = applyReuse(value, options);
                } else if (value === null && o.fallbackOnNull === true) {
                    value = undefined;
                } else if (value !== null) {
                    if (!o.returnObjectTrees && !options.returnObjectTrees) {
                        if (o.objectTreeKeyHandler && typeof o.objectTreeKeyHandler == 'function') {
                            value = o.objectTreeKeyHandler(key, value, l, ns, options);
                        } else {
                            value = 'key \'' + ns + ':' + key + ' (' + l + ')\' ' +
                                'returned an object instead of string.';
                            f.log(value);
                        }
                    } else if (valueType !== '[object Number]' && valueType !== '[object Function]' && valueType !== '[object RegExp]') {
                        var copy = (valueType === '[object Array]') ? [] : {}; // apply child translation on a copy
                        f.each(value, function(m) {
                            copy[m] = _translate(ns + nsseparator + key + keyseparator + m, options);
                        });
                        value = copy;
                    }
                }
    
                if (typeof value === 'string' && value.trim() === '' && o.fallbackOnEmpty === true)
                    value = undefined;
    
                found = value;
            }
        }
    
        if (found === undefined && !options.isFallbackLookup && (o.fallbackToDefaultNS === true || (o.fallbackNS && o.fallbackNS.length > 0))) {
            // set flag for fallback lookup - avoid recursion
            options.isFallbackLookup = true;
    
            if (o.fallbackNS.length) {
    
                for (var y = 0, lenY = o.fallbackNS.length; y < lenY; y++) {
                    found = _find(o.fallbackNS[y] + nsseparator + key, options);
    
                    if (found || (found==="" && o.fallbackOnEmpty === false)) {
                        /* compare value without namespace */
                        var foundValue = found.indexOf(nsseparator) > -1 ? found.split(nsseparator)[1] : found
                          , notFoundValue = notFound.indexOf(nsseparator) > -1 ? notFound.split(nsseparator)[1] : notFound;
    
                        if (foundValue !== notFoundValue) break;
                    }
                }
            } else {
                options.ns = o.ns.defaultNs;
                found = _find(key, options); // fallback to default NS
            }
            options.isFallbackLookup = false;
        }
    
        return found;
    }
    function detectLanguage() {
        var detectedLng;
        var whitelist = o.lngWhitelist || [];
        var userLngChoices = [];
    
        // get from qs
        var qsParm = [];
        if (typeof window !== 'undefined') {
            (function() {
                var query = window.location.search.substring(1);
                var params = query.split('&');
                for (var i=0; i<params.length; i++) {
                    var pos = params[i].indexOf('=');
                    if (pos > 0) {
                        var key = params[i].substring(0,pos);
                        if (key == o.detectLngQS) {
                            userLngChoices.push(params[i].substring(pos+1));
                        }
                    }
                }
            })();
        }
    
        // get from cookie
        if (o.useCookie && typeof document !== 'undefined') {
            var c = f.cookie.read(o.cookieName);
            if (c) userLngChoices.push(c);
        }
    
        // get from localStorage
        if (o.detectLngFromLocalStorage && typeof window !== 'undefined' && window.localStorage) {
            var lang = f.localStorage.getItem('i18next_lng');
            if (lang) {
                userLngChoices.push(lang);
            }
        }
    
        // get from navigator
        if (typeof navigator !== 'undefined') {
            if (navigator.languages) { // chrome only; not an array, so can't use .push.apply instead of iterating
                for (var i=0;i<navigator.languages.length;i++) {
                    userLngChoices.push(navigator.languages[i]);
                }
            }
            if (navigator.userLanguage) {
                userLngChoices.push(navigator.userLanguage);
            }
            if (navigator.language) {
                userLngChoices.push(navigator.language);
            }
        }
    
        (function() {
            for (var i=0;i<userLngChoices.length;i++) {
                var lng = userLngChoices[i];
    
                if (lng.indexOf('-') > -1) {
                    var parts = lng.split('-');
                    lng = o.lowerCaseLng ?
                        parts[0].toLowerCase() +  '-' + parts[1].toLowerCase() :
                        parts[0].toLowerCase() +  '-' + parts[1].toUpperCase();
                }
    
                if (whitelist.length === 0 || whitelist.indexOf(lng) > -1) {
                    detectedLng = lng;
                    break;
                }
            }
        })();
    
        //fallback
        if (!detectedLng){
          detectedLng = o.fallbackLng[0];
        }
        
        return detectedLng;
    }
    // definition http://translate.sourceforge.net/wiki/l10n/pluralforms
    
    /* [code, name, numbers, pluralsType] */
    var _rules = [
        ["ach", "Acholi", [1,2], 1],
        ["af", "Afrikaans",[1,2], 2],
        ["ak", "Akan", [1,2], 1],
        ["am", "Amharic", [1,2], 1],
        ["an", "Aragonese",[1,2], 2],
        ["ar", "Arabic", [0,1,2,3,11,100],5],
        ["arn", "Mapudungun",[1,2], 1],
        ["ast", "Asturian", [1,2], 2],
        ["ay", "Aymará", [1], 3],
        ["az", "Azerbaijani",[1,2],2],
        ["be", "Belarusian",[1,2,5],4],
        ["bg", "Bulgarian",[1,2], 2],
        ["bn", "Bengali", [1,2], 2],
        ["bo", "Tibetan", [1], 3],
        ["br", "Breton", [1,2], 1],
        ["bs", "Bosnian", [1,2,5],4],
        ["ca", "Catalan", [1,2], 2],
        ["cgg", "Chiga", [1], 3],
        ["cs", "Czech", [1,2,5],6],
        ["csb", "Kashubian",[1,2,5],7],
        ["cy", "Welsh", [1,2,3,8],8],
        ["da", "Danish", [1,2], 2],
        ["de", "German", [1,2], 2],
        ["dev", "Development Fallback", [1,2], 2],
        ["dz", "Dzongkha", [1], 3],
        ["el", "Greek", [1,2], 2],
        ["en", "English", [1,2], 2],
        ["eo", "Esperanto",[1,2], 2],
        ["es", "Spanish", [1,2], 2],
        ["es_ar","Argentinean Spanish", [1,2], 2],
        ["et", "Estonian", [1,2], 2],
        ["eu", "Basque", [1,2], 2],
        ["fa", "Persian", [1], 3],
        ["fi", "Finnish", [1,2], 2],
        ["fil", "Filipino", [1,2], 1],
        ["fo", "Faroese", [1,2], 2],
        ["fr", "French", [1,2], 9],
        ["fur", "Friulian", [1,2], 2],
        ["fy", "Frisian", [1,2], 2],
        ["ga", "Irish", [1,2,3,7,11],10],
        ["gd", "Scottish Gaelic",[1,2,3,20],11],
        ["gl", "Galician", [1,2], 2],
        ["gu", "Gujarati", [1,2], 2],
        ["gun", "Gun", [1,2], 1],
        ["ha", "Hausa", [1,2], 2],
        ["he", "Hebrew", [1,2], 2],
        ["hi", "Hindi", [1,2], 2],
        ["hr", "Croatian", [1,2,5],4],
        ["hu", "Hungarian",[1,2], 2],
        ["hy", "Armenian", [1,2], 2],
        ["ia", "Interlingua",[1,2],2],
        ["id", "Indonesian",[1], 3],
        ["is", "Icelandic",[1,2], 12],
        ["it", "Italian", [1,2], 2],
        ["ja", "Japanese", [1], 3],
        ["jbo", "Lojban", [1], 3],
        ["jv", "Javanese", [0,1], 13],
        ["ka", "Georgian", [1], 3],
        ["kk", "Kazakh", [1], 3],
        ["km", "Khmer", [1], 3],
        ["kn", "Kannada", [1,2], 2],
        ["ko", "Korean", [1], 3],
        ["ku", "Kurdish", [1,2], 2],
        ["kw", "Cornish", [1,2,3,4],14],
        ["ky", "Kyrgyz", [1], 3],
        ["lb", "Letzeburgesch",[1,2],2],
        ["ln", "Lingala", [1,2], 1],
        ["lo", "Lao", [1], 3],
        ["lt", "Lithuanian",[1,2,10],15],
        ["lv", "Latvian", [1,2,0],16],
        ["mai", "Maithili", [1,2], 2],
        ["mfe", "Mauritian Creole",[1,2],1],
        ["mg", "Malagasy", [1,2], 1],
        ["mi", "Maori", [1,2], 1],
        ["mk", "Macedonian",[1,2],17],
        ["ml", "Malayalam",[1,2], 2],
        ["mn", "Mongolian",[1,2], 2],
        ["mnk", "Mandinka", [0,1,2],18],
        ["mr", "Marathi", [1,2], 2],
        ["ms", "Malay", [1], 3],
        ["mt", "Maltese", [1,2,11,20],19],
        ["nah", "Nahuatl", [1,2], 2],
        ["nap", "Neapolitan",[1,2], 2],
        ["nb", "Norwegian Bokmal",[1,2],2],
        ["ne", "Nepali", [1,2], 2],
        ["nl", "Dutch", [1,2], 2],
        ["nn", "Norwegian Nynorsk",[1,2],2],
        ["no", "Norwegian",[1,2], 2],
        ["nso", "Northern Sotho",[1,2],2],
        ["oc", "Occitan", [1,2], 1],
        ["or", "Oriya", [2,1], 2],
        ["pa", "Punjabi", [1,2], 2],
        ["pap", "Papiamento",[1,2], 2],
        ["pl", "Polish", [1,2,5],7],
        ["pms", "Piemontese",[1,2], 2],
        ["ps", "Pashto", [1,2], 2],
        ["pt", "Portuguese",[1,2], 2],
        ["pt_br","Brazilian Portuguese",[1,2], 2],
        ["rm", "Romansh", [1,2], 2],
        ["ro", "Romanian", [1,2,20],20],
        ["ru", "Russian", [1,2,5],4],
        ["sah", "Yakut", [1], 3],
        ["sco", "Scots", [1,2], 2],
        ["se", "Northern Sami",[1,2], 2],
        ["si", "Sinhala", [1,2], 2],
        ["sk", "Slovak", [1,2,5],6],
        ["sl", "Slovenian",[5,1,2,3],21],
        ["so", "Somali", [1,2], 2],
        ["son", "Songhay", [1,2], 2],
        ["sq", "Albanian", [1,2], 2],
        ["sr", "Serbian", [1,2,5],4],
        ["su", "Sundanese",[1], 3],
        ["sv", "Swedish", [1,2], 2],
        ["sw", "Swahili", [1,2], 2],
        ["ta", "Tamil", [1,2], 2],
        ["te", "Telugu", [1,2], 2],
        ["tg", "Tajik", [1,2], 1],
        ["th", "Thai", [1], 3],
        ["ti", "Tigrinya", [1,2], 1],
        ["tk", "Turkmen", [1,2], 2],
        ["tr", "Turkish", [1,2], 1],
        ["tt", "Tatar", [1], 3],
        ["ug", "Uyghur", [1], 3],
        ["uk", "Ukrainian",[1,2,5],4],
        ["ur", "Urdu", [1,2], 2],
        ["uz", "Uzbek", [1,2], 1],
        ["vi", "Vietnamese",[1], 3],
        ["wa", "Walloon", [1,2], 1],
        ["wo", "Wolof", [1], 3],
        ["yo", "Yoruba", [1,2], 2],
        ["zh", "Chinese", [1], 3]
    ];
    
    var _rulesPluralsTypes = {
        1: function(n) {return Number(n > 1);},
        2: function(n) {return Number(n != 1);},
        3: function(n) {return 0;},
        4: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);},
        5: function(n) {return Number(n===0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5);},
        6: function(n) {return Number((n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2);},
        7: function(n) {return Number(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);},
        8: function(n) {return Number((n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3);},
        9: function(n) {return Number(n >= 2);},
        10: function(n) {return Number(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : 4) ;},
        11: function(n) {return Number((n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3);},
        12: function(n) {return Number(n%10!=1 || n%100==11);},
        13: function(n) {return Number(n !== 0);},
        14: function(n) {return Number((n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : 3);},
        15: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);},
        16: function(n) {return Number(n%10==1 && n%100!=11 ? 0 : n !== 0 ? 1 : 2);},
        17: function(n) {return Number(n==1 || n%10==1 ? 0 : 1);},
        18: function(n) {return Number(n==0 ? 0 : n==1 ? 1 : 2);},
        19: function(n) {return Number(n==1 ? 0 : n===0 || ( n%100>1 && n%100<11) ? 1 : (n%100>10 && n%100<20 ) ? 2 : 3);},
        20: function(n) {return Number(n==1 ? 0 : (n===0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2);},
        21: function(n) {return Number(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0); }
    };
    
    var pluralExtensions = {
    
        rules: (function () {
            var l, rules = {};
            for (l=_rules.length; l-- ;) {
                rules[_rules[l][0]] = {
                    name: _rules[l][1],
                    numbers: _rules[l][2],
                    plurals: _rulesPluralsTypes[_rules[l][3]]
                }
            }
            return rules;
        }()),
    
        // you can add your own pluralExtensions
        addRule: function(lng, obj) {
            pluralExtensions.rules[lng] = obj;
        },
    
        setCurrentLng: function(lng) {
            if (!pluralExtensions.currentRule || pluralExtensions.currentRule.lng !== lng) {
                var parts = lng.split('-');
    
                pluralExtensions.currentRule = {
                    lng: lng,
                    rule: pluralExtensions.rules[parts[0]]
                };
            }
        },
    
        needsPlural: function(lng, count) {
            var parts = lng.split('-');
    
            var ext;
            if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
                ext = pluralExtensions.currentRule.rule; 
            } else {
                ext = pluralExtensions.rules[parts[f.getCountyIndexOfLng(lng)]];
            }
    
            if (ext && ext.numbers.length <= 1) {
                return false;
            } else {
                return this.get(lng, count) !== 1;
            }
        },
    
        get: function(lng, count) {
            var parts = lng.split('-');
    
            function getResult(l, c) {
                var ext;
                if (pluralExtensions.currentRule && pluralExtensions.currentRule.lng === lng) {
                    ext = pluralExtensions.currentRule.rule; 
                } else {
                    ext = pluralExtensions.rules[l];
                }
                if (ext) {
                    var i;
                    if (ext.noAbs) {
                        i = ext.plurals(c);
                    } else {
                        i = ext.plurals(Math.abs(c));
                    }
                    
                    var number = ext.numbers[i];
                    if (ext.numbers.length === 2 && ext.numbers[0] === 1) {
                        if (number === 2) { 
                            number = -1; // regular plural
                        } else if (number === 1) {
                            number = 1; // singular
                        }
                    }//console.log(count + '-' + number);
                    return number;
                } else {
                    return c === 1 ? '1' : '-1';
                }
            }
                        
            return getResult(parts[f.getCountyIndexOfLng(lng)], count);
        }
    
    };
    var postProcessors = {};
    var addPostProcessor = function(name, fc) {
        postProcessors[name] = fc;
    };
    // sprintf support
    var sprintf = (function() {
        function get_type(variable) {
            return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
        }
        function str_repeat(input, multiplier) {
            for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
            return output.join('');
        }
    
        var str_format = function() {
            if (!str_format.cache.hasOwnProperty(arguments[0])) {
                str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
            }
            return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
        };
    
        str_format.format = function(parse_tree, argv) {
            var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
            for (i = 0; i < tree_length; i++) {
                node_type = get_type(parse_tree[i]);
                if (node_type === 'string') {
                    output.push(parse_tree[i]);
                }
                else if (node_type === 'array') {
                    match = parse_tree[i]; // convenience purposes only
                    if (match[2]) { // keyword argument
                        arg = argv[cursor];
                        for (k = 0; k < match[2].length; k++) {
                            if (!arg.hasOwnProperty(match[2][k])) {
                                throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
                            }
                            arg = arg[match[2][k]];
                        }
                    }
                    else if (match[1]) { // positional argument (explicit)
                        arg = argv[match[1]];
                    }
                    else { // positional argument (implicit)
                        arg = argv[cursor++];
                    }
    
                    if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
                        throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
                    }
                    switch (match[8]) {
                        case 'b': arg = arg.toString(2); break;
                        case 'c': arg = String.fromCharCode(arg); break;
                        case 'd': arg = parseInt(arg, 10); break;
                        case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
                        case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
                        case 'o': arg = arg.toString(8); break;
                        case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
                        case 'u': arg = Math.abs(arg); break;
                        case 'x': arg = arg.toString(16); break;
                        case 'X': arg = arg.toString(16).toUpperCase(); break;
                    }
                    arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
                    pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
                    pad_length = match[6] - String(arg).length;
                    pad = match[6] ? str_repeat(pad_character, pad_length) : '';
                    output.push(match[5] ? arg + pad : pad + arg);
                }
            }
            return output.join('');
        };
    
        str_format.cache = {};
    
        str_format.parse = function(fmt) {
            var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
            while (_fmt) {
                if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
                    parse_tree.push(match[0]);
                }
                else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
                    parse_tree.push('%');
                }
                else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
                    if (match[2]) {
                        arg_names |= 1;
                        var field_list = [], replacement_field = match[2], field_match = [];
                        if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
                            field_list.push(field_match[1]);
                            while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
                                if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
                                    field_list.push(field_match[1]);
                                }
                                else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
                                    field_list.push(field_match[1]);
                                }
                                else {
                                    throw('[sprintf] huh?');
                                }
                            }
                        }
                        else {
                            throw('[sprintf] huh?');
                        }
                        match[2] = field_list;
                    }
                    else {
                        arg_names |= 2;
                    }
                    if (arg_names === 3) {
                        throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
                    }
                    parse_tree.push(match);
                }
                else {
                    throw('[sprintf] huh?');
                }
                _fmt = _fmt.substring(match[0].length);
            }
            return parse_tree;
        };
    
        return str_format;
    })();
    
    var vsprintf = function(fmt, argv) {
        argv.unshift(fmt);
        return sprintf.apply(null, argv);
    };
    
    addPostProcessor("sprintf", function(val, key, opts) {
        if (!opts.sprintf) return val;
    
        if (Object.prototype.toString.apply(opts.sprintf) === '[object Array]') {
            return vsprintf(val, opts.sprintf);
        } else if (typeof opts.sprintf === 'object') {
            return sprintf(val, opts.sprintf);
        }
    
        return val;
    });
    // public api interface
    i18n.init = init;
    i18n.isInitialized = isInitialized;
    i18n.setLng = setLng;
    i18n.preload = preload;
    i18n.addResourceBundle = addResourceBundle;
    i18n.hasResourceBundle = hasResourceBundle;
    i18n.getResourceBundle = getResourceBundle;
    i18n.addResource = addResource;
    i18n.addResources = addResources;
    i18n.removeResourceBundle = removeResourceBundle;
    i18n.loadNamespace = loadNamespace;
    i18n.loadNamespaces = loadNamespaces;
    i18n.setDefaultNamespace = setDefaultNamespace;
    i18n.t = translate;
    i18n.translate = translate;
    i18n.exists = exists;
    i18n.detectLanguage = f.detectLanguage;
    i18n.pluralExtensions = pluralExtensions;
    i18n.sync = sync;
    i18n.functions = f;
    i18n.lng = lng;
    i18n.dir = dir;
    i18n.addPostProcessor = addPostProcessor;
    i18n.applyReplacement = f.applyReplacement;
    i18n.options = o;
    i18n.noConflict = noConflict;

})(typeof exports === 'undefined' ? window : exports);;
// i18next, v1.7.1
// Copyright (c)2013 Jan Mühlemann (jamuhl).
// Distributed under MIT license
// http://i18next.com
!function(){function a(a,b){if(!b||"function"==typeof b)return a;for(var c in b)a[c]=b[c];return a}function b(a,b,c){var d,e=0,f=a.length,g=void 0===f||"function"==typeof a;if(c)if(g){for(d in a)if(b.apply(a[d],c)===!1)break}else for(;f>e&&b.apply(a[e++],c)!==!1;);else if(g){for(d in a)if(b.call(a[d],d,a[d])===!1)break}else for(;f>e&&b.call(a[e],e,a[e++])!==!1;);return a}function c(a){return"string"==typeof a?a.replace(/[&<>"'\/]/g,function(a){return H[a]}):a}function d(a){var b=function(a){if(window.XMLHttpRequest)return a(null,new XMLHttpRequest);if(window.ActiveXObject)try{return a(null,new ActiveXObject("Msxml2.XMLHTTP"))}catch(b){return a(null,new ActiveXObject("Microsoft.XMLHTTP"))}return a(new Error)},c=function(a){if("string"==typeof a)return a;var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(encodeURIComponent(c)+"="+encodeURIComponent(a[c]));return b.join("&")},d=function(a){a=a.replace(/\r\n/g,"\n");for(var b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);128>d?b+=String.fromCharCode(d):d>127&&2048>d?(b+=String.fromCharCode(192|d>>6),b+=String.fromCharCode(128|63&d)):(b+=String.fromCharCode(224|d>>12),b+=String.fromCharCode(128|63&d>>6),b+=String.fromCharCode(128|63&d))}return b},e=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a=d(a);var c,e,f,g,h,i,j,k="",l=0;do c=a.charCodeAt(l++),e=a.charCodeAt(l++),f=a.charCodeAt(l++),g=c>>2,h=(3&c)<<4|e>>4,i=(15&e)<<2|f>>6,j=63&f,isNaN(e)?i=j=64:isNaN(f)&&(j=64),k+=b.charAt(g)+b.charAt(h)+b.charAt(i)+b.charAt(j),c=e=f="",g=h=i=j="";while(l<a.length);return k},f=function(){for(var a=arguments[0],b=1;b<arguments.length;b++){var c=arguments[b];for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d])}return a},g=function(a,d,e,h){"function"==typeof e&&(h=e,e={}),e.cache=e.cache||!1,e.data=e.data||{},e.headers=e.headers||{},e.jsonp=e.jsonp||!1,e.async=void 0===e.async?!0:e.async;var i,j=f({accept:"*/*","content-type":"application/x-www-form-urlencoded;charset=UTF-8"},g.headers,e.headers);if(i="application/json"===j["content-type"]?JSON.stringify(e.data):c(e.data),"GET"===a){var k=[];if(i&&(k.push(i),i=null),e.cache||k.push("_="+(new Date).getTime()),e.jsonp&&(k.push("callback="+e.jsonp),k.push("jsonp="+e.jsonp)),k=k.join("&"),k.length>1&&(d+=d.indexOf("?")>-1?"&"+k:"?"+k),e.jsonp){var l=document.getElementsByTagName("head")[0],m=document.createElement("script");return m.type="text/javascript",m.src=d,l.appendChild(m),void 0}}b(function(b,c){if(b)return h(b);c.open(a,d,e.async);for(var f in j)j.hasOwnProperty(f)&&c.setRequestHeader(f,j[f]);c.onreadystatechange=function(){if(4===c.readyState){var a=c.responseText||"";if(!h)return;h(c.status,{text:function(){return a},json:function(){return JSON.parse(a)}})}},c.send(i)})},h={authBasic:function(a,b){g.headers.Authorization="Basic "+e(a+":"+b)},connect:function(a,b,c){return g("CONNECT",a,b,c)},del:function(a,b,c){return g("DELETE",a,b,c)},get:function(a,b,c){return g("GET",a,b,c)},head:function(a,b,c){return g("HEAD",a,b,c)},headers:function(a){g.headers=a||{}},isAllowed:function(a,b,c){this.options(a,function(a,d){c(-1!==d.text().indexOf(b))})},options:function(a,b,c){return g("OPTIONS",a,b,c)},patch:function(a,b,c){return g("PATCH",a,b,c)},post:function(a,b,c){return g("POST",a,b,c)},put:function(a,b,c){return g("PUT",a,b,c)},trace:function(a,b,c){return g("TRACE",a,b,c)}},i=a.type?a.type.toLowerCase():"get";h[i](a.url,a,function(b,c){200===b?a.success(c.json(),b,null):a.error(c.text(),b,null)})}function e(a,b){"function"==typeof a&&(b=a,a={}),a=a||{},K.extend(G,a),delete G.fixLng,"string"==typeof G.ns&&(G.ns={namespaces:[G.ns],defaultNs:G.ns}),"string"==typeof G.fallbackNS&&(G.fallbackNS=[G.fallbackNS]),G.interpolationPrefixEscaped=K.regexEscape(G.interpolationPrefix),G.interpolationSuffixEscaped=K.regexEscape(G.interpolationSuffix),G.lng||(G.lng=K.detectLanguage()),G.lng?G.useCookie&&K.cookie.create(G.cookieName,G.lng,G.cookieExpirationTime,G.cookieDomain):(G.lng=G.fallbackLng,G.useCookie&&K.cookie.remove(G.cookieName)),E=K.toLanguages(G.lng),y=E[0],K.log("currentLng set to: "+y);var c=s;a.fixLng&&(c=function(a,b){return b=b||{},b.lng=b.lng||c.lng,s(a,b)},c.lng=y),M.setCurrentLng(y),A&&G.setJqueryExt&&m();var d;if(A&&A.Deferred&&(d=A.Deferred()),!G.resStore){var e=K.toLanguages(G.lng);"string"==typeof G.preload&&(G.preload=[G.preload]);for(var f=0,g=G.preload.length;g>f;f++)for(var h=K.toLanguages(G.preload[f]),i=0,j=h.length;j>i;i++)e.indexOf(h[i])<0&&e.push(h[i]);return B.sync.load(e,G,function(a,e){C=e,F=!0,b&&b(c),d&&d.resolve(c)}),d?d.promise():void 0}return C=G.resStore,F=!0,b&&b(c),d&&d.resolve(c),d?d.promise():void 0}function f(a,b){"string"==typeof a&&(a=[a]);for(var c=0,d=a.length;d>c;c++)G.preload.indexOf(a[c])<0&&G.preload.push(a[c]);return e(b)}function g(a,b,c){"string"!=typeof b?(c=b,b=G.ns.defaultNs):G.ns.namespaces.indexOf(b)<0&&G.ns.namespaces.push(b),C[a]=C[a]||{},C[a][b]=C[a][b]||{},K.extend(C[a][b],c)}function h(a){G.ns.defaultNs=a}function i(a,b){j([a],b)}function j(a,b){var c={dynamicLoad:G.dynamicLoad,resGetPath:G.resGetPath,getAsync:G.getAsync,customLoad:G.customLoad,ns:{namespaces:a,defaultNs:""}},d=K.toLanguages(G.lng);"string"==typeof G.preload&&(G.preload=[G.preload]);for(var e=0,f=G.preload.length;f>e;e++)for(var g=K.toLanguages(G.preload[e]),h=0,i=g.length;i>h;h++)d.indexOf(g[h])<0&&d.push(g[h]);for(var j=[],k=0,l=d.length;l>k;k++){var m=!1,n=C[d[k]];if(n)for(var o=0,p=a.length;p>o;o++)n[a[o]]||(m=!0);else m=!0;m&&j.push(d[k])}j.length?B.sync._fetch(j,c,function(c,d){var e=a.length*j.length;K.each(a,function(a,c){G.ns.namespaces.indexOf(c)<0&&G.ns.namespaces.push(c),K.each(j,function(a,f){C[f]=C[f]||{},C[f][c]=d[f][c],e--,0===e&&b&&(G.useLocalStorage&&B.sync._storeLocal(C),b())})})}):b&&b()}function k(a,b,c){return"function"==typeof b&&(c=b,b={}),b.lng=a,e(b,c)}function l(){return y}function m(){function a(a,b,c){if(0!==b.length){var d="text";if(0===b.indexOf("[")){var e=b.split("]");b=e[1],d=e[0].substr(1,e[0].length-1)}b.indexOf(";")===b.length-1&&(b=b.substr(0,b.length-2));var f;"html"===d?(f=G.defaultValueFromContent?A.extend({defaultValue:a.html()},c):c,a.html(A.t(b,f))):"text"===d?(f=G.defaultValueFromContent?A.extend({defaultValue:a.text()},c):c,a.text(A.t(b,f))):(f=G.defaultValueFromContent?A.extend({defaultValue:a.attr(d)},c):c,a.attr(d,A.t(b,f)))}}function b(b,c){var d=b.attr(G.selectorAttr);if(d||"undefined"==typeof d||d===!1||(d=b.text()||b.val()),d){var e=b,f=b.data("i18n-target");if(f&&(e=b.find(f)||b),c||G.useDataAttrOptions!==!0||(c=b.data("i18n-options")),c=c||{},d.indexOf(";")>=0){var g=d.split(";");A.each(g,function(b,d){""!==d&&a(e,d,c)})}else a(e,d,c);G.useDataAttrOptions===!0&&b.data("i18n-options",c)}}A.t=A.t||s,A.fn.i18n=function(a){return this.each(function(){b(A(this),a);var c=A(this).find("["+G.selectorAttr+"]");c.each(function(){b(A(this),a)})})}}function n(a,b,c,d){if(!a)return a;if(d=d||b,a.indexOf(d.interpolationPrefix||G.interpolationPrefix)<0)return a;var e=d.interpolationPrefix?K.regexEscape(d.interpolationPrefix):G.interpolationPrefixEscaped,f=d.interpolationSuffix?K.regexEscape(d.interpolationSuffix):G.interpolationSuffixEscaped,g="HTML"+f;return K.each(b,function(b,h){var i=c?c+G.keyseparator+b:b;"object"==typeof h&&null!==h?a=n(a,h,i,d):d.escapeInterpolation||G.escapeInterpolation?(a=a.replace(new RegExp([e,i,g].join(""),"g"),h),a=a.replace(new RegExp([e,i,f].join(""),"g"),K.escape(h))):a=a.replace(new RegExp([e,i,f].join(""),"g"),h)}),a}function o(a,b){var c=",",d="{",e="}",f=K.extend({},b);for(delete f.postProcess;-1!=a.indexOf(G.reusePrefix)&&(D++,!(D>G.maxRecursion));){var g=a.lastIndexOf(G.reusePrefix),h=a.indexOf(G.reuseSuffix,g)+G.reuseSuffix.length,i=a.substring(g,h),j=i.replace(G.reusePrefix,"").replace(G.reuseSuffix,"");if(-1!=j.indexOf(c)){var k=j.indexOf(c);if(-1!=j.indexOf(d,k)&&-1!=j.indexOf(e,k)){var l=j.indexOf(d,k),m=j.indexOf(e,l)+e.length;try{f=K.extend(f,JSON.parse(j.substring(l,m))),j=j.substring(0,k)}catch(n){}}}var o=v(j,f);a=a.replace(i,o)}return a}function p(a){return a.context&&"string"==typeof a.context}function q(a){return void 0!==a.count&&"string"!=typeof a.count&&1!==a.count}function r(a,b){b=b||{};var c=t(a,b),d=w(a,b);return void 0!==d||d===c}function s(a,b){return F?(D=0,v.apply(null,arguments)):(K.log("i18next not finished initialization. you might have called t function before loading resources finished."),b.defaultValue||"")}function t(a,b){return void 0!==b.defaultValue?b.defaultValue:a}function u(){for(var a=[],b=1;b<arguments.length;b++)a.push(arguments[b]);return{postProcess:"sprintf",sprintf:a}}function v(a,b){"string"==typeof b?"sprintf"===G.shortcutFunction?b=u.apply(null,arguments):"defaultValue"===G.shortcutFunction&&(b={defaultValue:b}):b=b||{},"string"==typeof a&&(a=[a]);for(var c=null,d=0;d<a.length&&(c=a[d],!r(c));d++);var e,f=t(c,b),g=w(c,b),h=b.lng?K.toLanguages(b.lng):E,i=b.ns||G.ns.defaultNs;c.indexOf(G.nsseparator)>-1&&(e=c.split(G.nsseparator),i=e[0],c=e[1]),void 0===g&&G.sendMissing&&(b.lng?L.postMissing(h[0],i,c,f,h):L.postMissing(G.lng,i,c,f,h));var j=b.postProcess||G.postProcess;void 0!==g&&j&&N[j]&&(g=N[j](g,c,b));var k=f;if(f.indexOf(G.nsseparator)>-1&&(e=f.split(G.nsseparator),k=e[1]),k===c&&G.parseMissingKey&&(f=G.parseMissingKey(f)),void 0===g&&(f=n(f,b),f=o(f,b),j&&N[j])){var l=t(c,b);g=N[j](l,c,b)}return void 0!==g?g:f}function w(a,b){b=b||{};var c,d,e=t(a,b),f=E;if(!C)return e;if(b.lng&&(f=K.toLanguages(b.lng),!C[f[0]])){var g=G.getAsync;G.getAsync=!1,B.sync.load(f,G,function(a,b){K.extend(C,b),G.getAsync=g})}var h=b.ns||G.ns.defaultNs;if(a.indexOf(G.nsseparator)>-1){var i=a.split(G.nsseparator);h=i[0],a=i[1]}if(p(b)){c=K.extend({},b),delete c.context,c.defaultValue=G.contextNotFound;var j=h+G.nsseparator+a+"_"+b.context;if(d=s(j,c),d!=G.contextNotFound)return n(d,{context:b.context})}if(q(b)){c=K.extend({},b),delete c.count,c.defaultValue=G.pluralNotFound;var k=h+G.nsseparator+a+G.pluralSuffix,l=M.get(f[0],b.count);if(l>=0?k=k+"_"+l:1===l&&(k=h+G.nsseparator+a),d=s(k,c),d!=G.pluralNotFound)return n(d,{count:b.count,interpolationPrefix:b.interpolationPrefix,interpolationSuffix:b.interpolationSuffix})}for(var m,r=a.split(G.keyseparator),u=0,x=f.length;x>u&&void 0===m;u++){for(var y=f[u],z=0,A=C[y]&&C[y][h];r[z];)A=A&&A[r[z]],z++;if(void 0!==A){if("string"==typeof A)A=n(A,b),A=o(A,b);else if("[object Array]"!==Object.prototype.toString.apply(A)||G.returnObjectTrees||b.returnObjectTrees){if(null===A&&G.fallbackOnNull===!0)A=void 0;else if(null!==A)if(G.returnObjectTrees||b.returnObjectTrees){if("number"!=typeof A){var D={};K.each(A,function(c){D[c]=v(h+G.nsseparator+a+G.keyseparator+c,b)}),A=D}}else A="key '"+h+":"+a+" ("+y+")' "+"returned a object instead of string.",K.log(A)}else A=A.join("\n"),A=n(A,b),A=o(A,b);m=A}}if(void 0===m&&!b.isFallbackLookup&&(G.fallbackToDefaultNS===!0||G.fallbackNS&&G.fallbackNS.length>0))if(b.isFallbackLookup=!0,G.fallbackNS.length){for(var F=0,H=G.fallbackNS.length;H>F;F++)if(m=w(G.fallbackNS[F]+G.nsseparator+a,b)){var I=m.indexOf(G.nsseparator)>-1?m.split(G.nsseparator)[1]:m,J=e.indexOf(G.nsseparator)>-1?e.split(G.nsseparator)[1]:e;if(I!==J)break}}else m=w(a,b);return m}function x(){var a,b=[];if("undefined"!=typeof window&&(!function(){for(var a=window.location.search.substring(1),c=a.split("&"),d=0;d<c.length;d++){var e=c[d].indexOf("=");if(e>0){var f=c[d].substring(0,e),g=c[d].substring(e+1);b[f]=g}}}(),b[G.detectLngQS]&&(a=b[G.detectLngQS])),!a&&"undefined"!=typeof document&&G.useCookie){var c=K.cookie.read(G.cookieName);c&&(a=c)}return a||"undefined"==typeof navigator||(a=navigator.language?navigator.language:navigator.userLanguage),a}Array.prototype.indexOf||(Array.prototype.indexOf=function(a){"use strict";if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>0&&(d=Number(arguments[1]),d!=d?d=0:0!=d&&1/0!=d&&d!=-1/0&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1}),Array.prototype.lastIndexOf||(Array.prototype.lastIndexOf=function(a){"use strict";if(null==this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=c;arguments.length>1&&(d=Number(arguments[1]),d!=d?d=0:0!=d&&d!=1/0&&d!=-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d))));for(var e=d>=0?Math.min(d,c-1):c-Math.abs(d);e>=0;e--)if(e in b&&b[e]===a)return e;return-1});var y,z=this,A=z.jQuery||z.Zepto,B={},C={},D=0,E=[],F=!1;"undefined"!=typeof module&&module.exports?module.exports=B:(A&&(A.i18n=A.i18n||B),z.i18n=z.i18n||B);var G={lng:void 0,load:"all",preload:[],lowerCaseLng:!1,returnObjectTrees:!1,fallbackLng:"dev",fallbackNS:[],detectLngQS:"setLng",ns:"translation",fallbackOnNull:!0,fallbackToDefaultNS:!1,nsseparator:":",keyseparator:".",selectorAttr:"data-i18n",debug:!1,resGetPath:"locales/__lng__/__ns__.json",resPostPath:"locales/add/__lng__/__ns__",getAsync:!0,postAsync:!0,resStore:void 0,useLocalStorage:!1,localStorageExpirationTime:6048e5,dynamicLoad:!1,sendMissing:!1,sendMissingTo:"fallback",sendType:"POST",interpolationPrefix:"__",interpolationSuffix:"__",reusePrefix:"$t(",reuseSuffix:")",pluralSuffix:"_plural",pluralNotFound:["plural_not_found",Math.random()].join(""),contextNotFound:["context_not_found",Math.random()].join(""),escapeInterpolation:!1,setJqueryExt:!0,defaultValueFromContent:!0,useDataAttrOptions:!1,cookieExpirationTime:void 0,useCookie:!0,cookieName:"i18next",cookieDomain:void 0,postProcess:void 0,parseMissingKey:void 0,shortcutFunction:"sprintf"},H={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;"},I={create:function(a,b,c,d){var e;if(c){var f=new Date;f.setTime(f.getTime()+1e3*60*c),e="; expires="+f.toGMTString()}else e="";d=d?"domain="+d+";":"",document.cookie=a+"="+b+e+";"+d+"path=/"},read:function(a){for(var b=a+"=",c=document.cookie.split(";"),d=0;d<c.length;d++){for(var e=c[d];" "==e.charAt(0);)e=e.substring(1,e.length);if(0===e.indexOf(b))return e.substring(b.length,e.length)}return null},remove:function(a){this.create(a,"",-1)}},J={create:function(){},read:function(){return null},remove:function(){}},K={extend:A?A.extend:a,each:A?A.each:b,ajax:A?A.ajax:d,cookie:"undefined"!=typeof document?I:J,detectLanguage:x,escape:c,log:function(a){G.debug&&"undefined"!=typeof console&&console.log(a)},toLanguages:function(a){var b=[];if("string"==typeof a&&a.indexOf("-")>-1){var c=a.split("-");a=G.lowerCaseLng?c[0].toLowerCase()+"-"+c[1].toLowerCase():c[0].toLowerCase()+"-"+c[1].toUpperCase(),"unspecific"!==G.load&&b.push(a),"current"!==G.load&&b.push(c[0])}else b.push(a);return-1===b.indexOf(G.fallbackLng)&&G.fallbackLng&&b.push(G.fallbackLng),b},regexEscape:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}};K.applyReplacement=n;var L={load:function(a,b,c){b.useLocalStorage?L._loadLocal(a,b,function(d,e){for(var f=[],g=0,h=a.length;h>g;g++)e[a[g]]||f.push(a[g]);f.length>0?L._fetch(f,b,function(a,b){K.extend(e,b),L._storeLocal(b),c(null,e)}):c(null,e)}):L._fetch(a,b,function(a,b){c(null,b)})},_loadLocal:function(a,b,c){var d={},e=(new Date).getTime();if(window.localStorage){var f=a.length;K.each(a,function(a,g){var h=window.localStorage.getItem("res_"+g);h&&(h=JSON.parse(h),h.i18nStamp&&h.i18nStamp+b.localStorageExpirationTime>e&&(d[g]=h)),f--,0===f&&c(null,d)})}},_storeLocal:function(a){if(window.localStorage)for(var b in a)a[b].i18nStamp=(new Date).getTime(),window.localStorage.setItem("res_"+b,JSON.stringify(a[b]))},_fetch:function(a,b,c){var d=b.ns,e={};if(b.dynamicLoad){var f=function(a,b){c(null,b)};if("function"==typeof b.customLoad)b.customLoad(a,d.namespaces,b,f);else{var g=n(b.resGetPath,{lng:a.join("+"),ns:d.namespaces.join("+")});K.ajax({url:g,success:function(a){K.log("loaded: "+g),f(null,a)},error:function(a,b,c){K.log("failed loading: "+g),f("failed loading resource.json error: "+c)},dataType:"json",async:b.getAsync})}}else{var h,i=d.namespaces.length*a.length;K.each(d.namespaces,function(d,f){K.each(a,function(a,d){var g=function(a,b){a&&(h=h||[],h.push(a)),e[d]=e[d]||{},e[d][f]=b,i--,0===i&&c(h,e)};"function"==typeof b.customLoad?b.customLoad(d,f,b,g):L._fetchOne(d,f,b,g)})})}},_fetchOne:function(a,b,c,d){var e=n(c.resGetPath,{lng:a,ns:b});K.ajax({url:e,success:function(a){K.log("loaded: "+e),d(null,a)},error:function(a,b,c){K.log("failed loading: "+e),d(c,{})},dataType:"json",async:c.getAsync})},postMissing:function(a,b,c,d,e){var f={};f[c]=d;var g=[];if("fallback"===G.sendMissingTo&&G.fallbackLng!==!1)g.push({lng:G.fallbackLng,url:n(G.resPostPath,{lng:G.fallbackLng,ns:b})});else if("current"===G.sendMissingTo||"fallback"===G.sendMissingTo&&G.fallbackLng===!1)g.push({lng:a,url:n(G.resPostPath,{lng:a,ns:b})});else if("all"===G.sendMissingTo)for(var h=0,i=e.length;i>h;h++)g.push({lng:e[h],url:n(G.resPostPath,{lng:e[h],ns:b})});for(var j=0,k=g.length;k>j;j++){var l=g[j];K.ajax({url:l.url,type:G.sendType,data:f,success:function(){K.log("posted missing key '"+c+"' to: "+l.url);for(var a=c.split("."),e=0,f=C[l.lng][b];a[e];)f=f[a[e]]=e===a.length-1?d:f[a[e]]||{},e++},error:function(){K.log("failed posting missing key '"+c+"' to: "+l.url)},dataType:"json",async:G.postAsync})}}},M={rules:{ach:{name:"Acholi",numbers:[1,2],plurals:function(a){return Number(a>1)}},af:{name:"Afrikaans",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ak:{name:"Akan",numbers:[1,2],plurals:function(a){return Number(a>1)}},am:{name:"Amharic",numbers:[1,2],plurals:function(a){return Number(a>1)}},an:{name:"Aragonese",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ar:{name:"Arabic",numbers:[0,1,2,3,11,100],plurals:function(a){return Number(0===a?0:1==a?1:2==a?2:a%100>=3&&10>=a%100?3:a%100>=11?4:5)}},arn:{name:"Mapudungun",numbers:[1,2],plurals:function(a){return Number(a>1)}},ast:{name:"Asturian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ay:{name:"Aymará",numbers:[1],plurals:function(){return 0}},az:{name:"Azerbaijani",numbers:[1,2],plurals:function(a){return Number(1!=a)}},be:{name:"Belarusian",numbers:[1,2,5],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},bg:{name:"Bulgarian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},bn:{name:"Bengali",numbers:[1,2],plurals:function(a){return Number(1!=a)}},bo:{name:"Tibetan",numbers:[1],plurals:function(){return 0}},br:{name:"Breton",numbers:[1,2],plurals:function(a){return Number(a>1)}},bs:{name:"Bosnian",numbers:[1,2,5],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},ca:{name:"Catalan",numbers:[1,2],plurals:function(a){return Number(1!=a)}},cgg:{name:"Chiga",numbers:[1],plurals:function(){return 0}},cs:{name:"Czech",numbers:[1,2,5],plurals:function(a){return Number(1==a?0:a>=2&&4>=a?1:2)}},csb:{name:"Kashubian",numbers:[1,2,5],plurals:function(a){return Number(1==a?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},cy:{name:"Welsh",numbers:[1,2,3,8],plurals:function(a){return Number(1==a?0:2==a?1:8!=a&&11!=a?2:3)}},da:{name:"Danish",numbers:[1,2],plurals:function(a){return Number(1!=a)}},de:{name:"German",numbers:[1,2],plurals:function(a){return Number(1!=a)}},dz:{name:"Dzongkha",numbers:[1],plurals:function(){return 0}},el:{name:"Greek",numbers:[1,2],plurals:function(a){return Number(1!=a)}},en:{name:"English",numbers:[1,2],plurals:function(a){return Number(1!=a)}},eo:{name:"Esperanto",numbers:[1,2],plurals:function(a){return Number(1!=a)}},es:{name:"Spanish",numbers:[1,2],plurals:function(a){return Number(1!=a)}},es_ar:{name:"Argentinean Spanish",numbers:[1,2],plurals:function(a){return Number(1!=a)}},et:{name:"Estonian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},eu:{name:"Basque",numbers:[1,2],plurals:function(a){return Number(1!=a)}},fa:{name:"Persian",numbers:[1],plurals:function(){return 0}},fi:{name:"Finnish",numbers:[1,2],plurals:function(a){return Number(1!=a)}},fil:{name:"Filipino",numbers:[1,2],plurals:function(a){return Number(a>1)}},fo:{name:"Faroese",numbers:[1,2],plurals:function(a){return Number(1!=a)}},fr:{name:"French",numbers:[1,2],plurals:function(a){return Number(a>1)}},fur:{name:"Friulian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},fy:{name:"Frisian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ga:{name:"Irish",numbers:[1,2,3,7,11],plurals:function(a){return Number(1==a?0:2==a?1:7>a?2:11>a?3:4)}},gd:{name:"Scottish Gaelic",numbers:[1,2,3,20],plurals:function(a){return Number(1==a||11==a?0:2==a||12==a?1:a>2&&20>a?2:3)}},gl:{name:"Galician",numbers:[1,2],plurals:function(a){return Number(1!=a)}},gu:{name:"Gujarati",numbers:[1,2],plurals:function(a){return Number(1!=a)}},gun:{name:"Gun",numbers:[1,2],plurals:function(a){return Number(a>1)}},ha:{name:"Hausa",numbers:[1,2],plurals:function(a){return Number(1!=a)}},he:{name:"Hebrew",numbers:[1,2],plurals:function(a){return Number(1!=a)}},hi:{name:"Hindi",numbers:[1,2],plurals:function(a){return Number(1!=a)}},hr:{name:"Croatian",numbers:[1,2,5],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},hu:{name:"Hungarian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},hy:{name:"Armenian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ia:{name:"Interlingua",numbers:[1,2],plurals:function(a){return Number(1!=a)}},id:{name:"Indonesian",numbers:[1],plurals:function(){return 0}},is:{name:"Icelandic",numbers:[1,2],plurals:function(a){return Number(1!=a%10||11==a%100)}},it:{name:"Italian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ja:{name:"Japanese",numbers:[1],plurals:function(){return 0}},jbo:{name:"Lojban",numbers:[1],plurals:function(){return 0}},jv:{name:"Javanese",numbers:[0,1],plurals:function(a){return Number(0!==a)}},ka:{name:"Georgian",numbers:[1],plurals:function(){return 0}},kk:{name:"Kazakh",numbers:[1],plurals:function(){return 0}},km:{name:"Khmer",numbers:[1],plurals:function(){return 0}},kn:{name:"Kannada",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ko:{name:"Korean",numbers:[1],plurals:function(){return 0}},ku:{name:"Kurdish",numbers:[1,2],plurals:function(a){return Number(1!=a)}},kw:{name:"Cornish",numbers:[1,2,3,4],plurals:function(a){return Number(1==a?0:2==a?1:3==a?2:3)}},ky:{name:"Kyrgyz",numbers:[1],plurals:function(){return 0}},lb:{name:"Letzeburgesch",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ln:{name:"Lingala",numbers:[1,2],plurals:function(a){return Number(a>1)}},lo:{name:"Lao",numbers:[1],plurals:function(){return 0}},lt:{name:"Lithuanian",numbers:[1,2,10],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&(10>a%100||a%100>=20)?1:2)}},lv:{name:"Latvian",numbers:[0,1,2],plurals:function(a){return Number(1==a%10&&11!=a%100?0:0!==a?1:2)}},mai:{name:"Maithili",numbers:[1,2],plurals:function(a){return Number(1!=a)}},mfe:{name:"Mauritian Creole",numbers:[1,2],plurals:function(a){return Number(a>1)}},mg:{name:"Malagasy",numbers:[1,2],plurals:function(a){return Number(a>1)}},mi:{name:"Maori",numbers:[1,2],plurals:function(a){return Number(a>1)}},mk:{name:"Macedonian",numbers:[1,2],plurals:function(a){return Number(1==a||1==a%10?0:1)}},ml:{name:"Malayalam",numbers:[1,2],plurals:function(a){return Number(1!=a)}},mn:{name:"Mongolian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},mnk:{name:"Mandinka",numbers:[0,1,2],plurals:function(a){return Number(1==a?1:2)}},mr:{name:"Marathi",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ms:{name:"Malay",numbers:[1],plurals:function(){return 0}},mt:{name:"Maltese",numbers:[1,2,11,20],plurals:function(a){return Number(1==a?0:0===a||a%100>1&&11>a%100?1:a%100>10&&20>a%100?2:3)}},nah:{name:"Nahuatl",numbers:[1,2],plurals:function(a){return Number(1!=a)}},nap:{name:"Neapolitan",numbers:[1,2],plurals:function(a){return Number(1!=a)}},nb:{name:"Norwegian Bokmal",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ne:{name:"Nepali",numbers:[1,2],plurals:function(a){return Number(1!=a)}},nl:{name:"Dutch",numbers:[1,2],plurals:function(a){return Number(1!=a)}},nn:{name:"Norwegian Nynorsk",numbers:[1,2],plurals:function(a){return Number(1!=a)}},no:{name:"Norwegian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},nso:{name:"Northern Sotho",numbers:[1,2],plurals:function(a){return Number(1!=a)}},oc:{name:"Occitan",numbers:[1,2],plurals:function(a){return Number(a>1)}},or:{name:"Oriya",numbers:[2,1],plurals:function(a){return Number(1!=a)}},pa:{name:"Punjabi",numbers:[1,2],plurals:function(a){return Number(1!=a)}},pap:{name:"Papiamento",numbers:[1,2],plurals:function(a){return Number(1!=a)}},pl:{name:"Polish",numbers:[1,2,5],plurals:function(a){return Number(1==a?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},pms:{name:"Piemontese",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ps:{name:"Pashto",numbers:[1,2],plurals:function(a){return Number(1!=a)}},pt:{name:"Portuguese",numbers:[1,2],plurals:function(a){return Number(1!=a)}},pt_br:{name:"Brazilian Portuguese",numbers:[1,2],plurals:function(a){return Number(1!=a)}},rm:{name:"Romansh",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ro:{name:"Romanian",numbers:[1,2,20],plurals:function(a){return Number(1==a?0:0===a||a%100>0&&20>a%100?1:2)}},ru:{name:"Russian",numbers:[1,2,5],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},sah:{name:"Yakut",numbers:[1],plurals:function(){return 0}},sco:{name:"Scots",numbers:[1,2],plurals:function(a){return Number(1!=a)}},se:{name:"Northern Sami",numbers:[1,2],plurals:function(a){return Number(1!=a)}},si:{name:"Sinhala",numbers:[1,2],plurals:function(a){return Number(1!=a)}},sk:{name:"Slovak",numbers:[1,2,5],plurals:function(a){return Number(1==a?0:a>=2&&4>=a?1:2)}},sl:{name:"Slovenian",numbers:[5,1,2,3],plurals:function(a){return Number(1==a%100?1:2==a%100?2:3==a%100||4==a%100?3:0)}},so:{name:"Somali",numbers:[1,2],plurals:function(a){return Number(1!=a)}},son:{name:"Songhay",numbers:[1,2],plurals:function(a){return Number(1!=a)}},sq:{name:"Albanian",numbers:[1,2],plurals:function(a){return Number(1!=a)}},sr:{name:"Serbian",numbers:[1,2,5],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},su:{name:"Sundanese",numbers:[1],plurals:function(){return 0}},sv:{name:"Swedish",numbers:[1,2],plurals:function(a){return Number(1!=a)}},sw:{name:"Swahili",numbers:[1,2],plurals:function(a){return Number(1!=a)}},ta:{name:"Tamil",numbers:[1,2],plurals:function(a){return Number(1!=a)}},te:{name:"Telugu",numbers:[1,2],plurals:function(a){return Number(1!=a)}},tg:{name:"Tajik",numbers:[1,2],plurals:function(a){return Number(a>1)}},th:{name:"Thai",numbers:[1],plurals:function(){return 0}},ti:{name:"Tigrinya",numbers:[1,2],plurals:function(a){return Number(a>1)}},tk:{name:"Turkmen",numbers:[1,2],plurals:function(a){return Number(1!=a)}},tr:{name:"Turkish",numbers:[1,2],plurals:function(a){return Number(a>1)}},tt:{name:"Tatar",numbers:[1],plurals:function(){return 0}},ug:{name:"Uyghur",numbers:[1],plurals:function(){return 0}},uk:{name:"Ukrainian",numbers:[1,2,5],plurals:function(a){return Number(1==a%10&&11!=a%100?0:a%10>=2&&4>=a%10&&(10>a%100||a%100>=20)?1:2)}},ur:{name:"Urdu",numbers:[1,2],plurals:function(a){return Number(1!=a)}},uz:{name:"Uzbek",numbers:[1,2],plurals:function(a){return Number(a>1)}},vi:{name:"Vietnamese",numbers:[1],plurals:function(){return 0}},wa:{name:"Walloon",numbers:[1,2],plurals:function(a){return Number(a>1)}},wo:{name:"Wolof",numbers:[1],plurals:function(){return 0}},yo:{name:"Yoruba",numbers:[1,2],plurals:function(a){return Number(1!=a)}},zh:{name:"Chinese",numbers:[1],plurals:function(){return 0}}},addRule:function(a,b){M.rules[a]=b},setCurrentLng:function(a){if(!M.currentRule||M.currentRule.lng!==a){var b=a.split("-");M.currentRule={lng:a,rule:M.rules[b[0]]}}},get:function(a,b){function c(b,c){var d;if(d=M.currentRule&&M.currentRule.lng===a?M.currentRule.rule:M.rules[b]){var e=d.plurals(c),f=d.numbers[e];return 2===d.numbers.length&&1===d.numbers[0]&&(2===f?f=-1:1===f&&(f=1)),f}return 1===c?"1":"-1"}var d=a.split("-");return c(d[0],b)}},N={},O=function(a,b){N[a]=b},P=function(){function a(a){return Object.prototype.toString.call(a).slice(8,-1).toLowerCase()}function b(a,b){for(var c=[];b>0;c[--b]=a);return c.join("")}var c=function(){return c.cache.hasOwnProperty(arguments[0])||(c.cache[arguments[0]]=c.parse(arguments[0])),c.format.call(null,c.cache[arguments[0]],arguments)};return c.format=function(c,d){var e,f,g,h,i,j,k,l=1,m=c.length,n="",o=[];for(f=0;m>f;f++)if(n=a(c[f]),"string"===n)o.push(c[f]);else if("array"===n){if(h=c[f],h[2])for(e=d[l],g=0;g<h[2].length;g++){if(!e.hasOwnProperty(h[2][g]))throw P('[sprintf] property "%s" does not exist',h[2][g]);e=e[h[2][g]]}else e=h[1]?d[h[1]]:d[l++];if(/[^s]/.test(h[8])&&"number"!=a(e))throw P("[sprintf] expecting number but found %s",a(e));switch(h[8]){case"b":e=e.toString(2);break;case"c":e=String.fromCharCode(e);break;case"d":e=parseInt(e,10);break;case"e":e=h[7]?e.toExponential(h[7]):e.toExponential();break;case"f":e=h[7]?parseFloat(e).toFixed(h[7]):parseFloat(e);break;case"o":e=e.toString(8);break;case"s":e=(e=String(e))&&h[7]?e.substring(0,h[7]):e;break;case"u":e=Math.abs(e);break;case"x":e=e.toString(16);break;case"X":e=e.toString(16).toUpperCase()}e=/[def]/.test(h[8])&&h[3]&&e>=0?"+"+e:e,j=h[4]?"0"==h[4]?"0":h[4].charAt(1):" ",k=h[6]-String(e).length,i=h[6]?b(j,k):"",o.push(h[5]?e+i:i+e)}return o.join("")},c.cache={},c.parse=function(a){for(var b=a,c=[],d=[],e=0;b;){if(null!==(c=/^[^\x25]+/.exec(b)))d.push(c[0]);else if(null!==(c=/^\x25{2}/.exec(b)))d.push("%");else{if(null===(c=/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(b)))throw"[sprintf] huh?";if(c[2]){e|=1;var f=[],g=c[2],h=[];if(null===(h=/^([a-z_][a-z_\d]*)/i.exec(g)))throw"[sprintf] huh?";for(f.push(h[1]);""!==(g=g.substring(h[0].length));)if(null!==(h=/^\.([a-z_][a-z_\d]*)/i.exec(g)))f.push(h[1]);else{if(null===(h=/^\[(\d+)\]/.exec(g)))throw"[sprintf] huh?";f.push(h[1])}c[2]=f}else e|=2;if(3===e)throw"[sprintf] mixing positional and named placeholders is not (yet) supported";d.push(c)}b=b.substring(c[0].length)}return d},c}(),Q=function(a,b){return b.unshift(a),P.apply(null,b)};O("sprintf",function(a,b,c){return c.sprintf?"[object Array]"===Object.prototype.toString.apply(c.sprintf)?Q(a,c.sprintf):"object"==typeof c.sprintf?P(a,c.sprintf):a:a}),B.init=e,B.setLng=k,B.preload=f,B.addResourceBundle=g,B.loadNamespace=i,B.loadNamespaces=j,B.setDefaultNamespace=h,B.t=s,B.translate=s,B.exists=r,B.detectLanguage=K.detectLanguage,B.pluralExtensions=M,B.sync=L,B.functions=K,B.lng=l,B.addPostProcessor=O,B.options=G}();;
var StringMask = (function () {
	var tokens = {
		'0': {pattern: /\d/, _default: '0'},
        'X': {pattern: /(\d|X)/, _default: '0'},
		'9': {pattern: /\d/, optional: true},
		'#': {pattern: /\d/, optional: true, recursive: true},
		'S': {pattern: /[a-zA-Z]/},
		'U': {pattern: /[a-zA-Z]/, transform: function (c) { return c.toLocaleUpperCase(); }},
		'L': {pattern: /[a-zA-Z]/, transform: function (c) { return c.toLocaleLowerCase(); }},
		'$': {escape: true}
	};
	var isEscaped = function(pattern, pos) {
		var count = 0;
		var i = pos - 1;
		var token = {escape: true};
		while (i >= 0 && token && token.escape) {
			token = tokens[pattern.charAt(i)];
			count += token && token.escape ? 1 : 0;
			i--;
		}
		return count > 0 && count%2 === 1;
	};
	var calcOptionalNumbersToUse = function(pattern, value) {
		var numbersInP = pattern.replace(/[^0]/g,'').length;
		var numbersInV = value.replace(/[^\d]/g,'').length;
		return numbersInV - numbersInP;
	};
	var concatChar = function(text, character, options, token) {
		if (token && typeof token.transform === 'function') character = token.transform(character);
		if (options.reverse) return character + text;
		return text + character;
	};
	var hasMoreTokens = function(pattern, pos, inc) {
		var pc = pattern.charAt(pos);
		var token = tokens[pc];
		if (pc === '') return false;
		return token && !token.escape ? true : hasMoreTokens(pattern, pos + inc, inc);
	};
	var insertChar = function(text, char, position) {
		var t = text.split('');
		t.splice(position >= 0 ? position: 0, 0, char);
		return t.join('');
	};
	var StringMask = function(pattern, opt) {
		this.options = opt || {};
		this.options = {
			reverse: this.options.reverse || false,
			usedefaults: this.options.usedefaults || this.options.reverse
		};
		this.pattern = pattern;

		StringMask.prototype.process = function proccess(value) {
			if (!value) return '';
			value = value + '';
			var pattern2 = this.pattern;
			var valid = true;
			var formatted = '';
			var valuePos = this.options.reverse ? value.length - 1 : 0;
			var optionalNumbersToUse = calcOptionalNumbersToUse(pattern2, value);
			var escapeNext = false;
			var recursive = [];
			var inRecursiveMode = false;

			var steps = {
				start: this.options.reverse ? pattern2.length - 1 : 0,
				end: this.options.reverse ? -1 : pattern2.length,
				inc: this.options.reverse ? -1 : 1
			};

			var continueCondition = function(options) {
				if (!inRecursiveMode && hasMoreTokens(pattern2, i, steps.inc)) {
					return true;
				} else if (!inRecursiveMode) {
					inRecursiveMode = recursive.length > 0;
				}

				if (inRecursiveMode) {
					var pc = recursive.shift();
					recursive.push(pc);
					if (options.reverse && valuePos >= 0) {
						i++;
						pattern2 = insertChar(pattern2, pc, i);
						return true;
					} else if (!options.reverse && valuePos < value.length) {
						pattern2 = insertChar(pattern2, pc, i);
						return true;
					}
				}
				return i < pattern2.length && i >= 0;
			};

			for (var i = steps.start; continueCondition(this.options); i = i + steps.inc) {
				var pc = pattern2.charAt(i);
				var vc = value.charAt(valuePos);
				var token = tokens[pc];
				if (!inRecursiveMode || vc) {
					if (this.options.reverse && isEscaped(pattern2, i)) {
						formatted = concatChar(formatted, pc, this.options, token);
						i = i + steps.inc;
						continue;
					} else if (!this.options.reverse && escapeNext) {
						formatted = concatChar(formatted, pc, this.options, token);
						escapeNext = false;
						continue;
					} else if (!this.options.reverse && token && token.escape) {
						escapeNext = true;
						continue;
					}
				}

				if (!inRecursiveMode && token && token.recursive) {
					recursive.push(pc);
				} else if (inRecursiveMode && !vc) {
					if (!token || !token.recursive) formatted = concatChar(formatted, pc, this.options, token);
					continue;
				} else if (recursive.length > 0 && token && !token.recursive) {
					// Recursive tokens most be the last tokens of the pattern
					valid = false;
					continue;
				} else if (!inRecursiveMode && recursive.length > 0 && !vc) {
					continue;
				}

				if (!token) {
					formatted = concatChar(formatted, pc, this.options, token);
					if (!inRecursiveMode && recursive.length) {
						recursive.push(pc);
					}
				} else if (token.optional) {
					if (token.pattern.test(vc) && optionalNumbersToUse) {
						formatted = concatChar(formatted, vc, this.options, token);
						valuePos = valuePos + steps.inc;
						optionalNumbersToUse--;
					} else if (recursive.length > 0 && vc) {
						valid = false;
						break;
					}
				} else if (token.pattern.test(vc)) {
					formatted = concatChar(formatted, vc, this.options, token);
					valuePos = valuePos + steps.inc;
				} else if (!vc && token._default && this.options.usedefaults) {
					formatted = concatChar(formatted, token._default, this.options, token);
				} else {
					valid = false;
					break;
				}
			}

			return {result: formatted, valid: valid};
		};

		StringMask.prototype.apply = function(value) {
			return this.process(value).result;
		};

		StringMask.prototype.validate = function(value) {
			return this.process(value).valid;
		};
	};

	StringMask.process = function(value, pattern, options) {
		return new StringMask(pattern, options).process(value);
	};

	StringMask.apply = function(value, pattern, options) {
		return new StringMask(pattern, options).apply(value);
	};

	StringMask.validate = function(value, pattern, options) {
		return new StringMask(pattern, options).validate(value);
	};

	return StringMask;
}());

/** Used to determine if values are of the language type Object */
var objectTypes = {
	'boolean': false,
	'function': true,
	'object': true,
	'number': false,
	'string': false,
	'undefined': false
};

if (objectTypes[typeof module]) {
	module.exports = StringMask;
}
;
/*
 * jQote2 - client-side Javascript templating engine
 * Copyright (C) 2010, aefxx
 * http://aefxx.com/
 *
 * Dual licensed under the WTFPL v2 or MIT (X11) licenses
 * WTFPL v2 Copyright (C) 2004, Sam Hocevar
 *
 * Date: Fri, May 4th, 2012
 * Version: 0.9.8
 */
(function ($) {
    var JQOTE2_TMPL_UNDEF_ERROR = 'UndefinedTemplateError',
        JQOTE2_TMPL_COMP_ERROR = 'TemplateCompilationError',
        JQOTE2_TMPL_EXEC_ERROR = 'TemplateExecutionError';

    var ARR = '[object Array]',
        STR = '[object String]',
        FUNC = '[object Function]';

    var n = 1, tag = '%',
        qreg = /^[^<]*(<[\w\W]+>)[^>]*$/,
        type_of = Object.prototype.toString;

    function raise(error, ext) {
        throw ($.extend(error, ext), error);
    }

    function dotted_ns(fn) {
        var ns = [];

        if (type_of.call(fn) !== ARR) return false;

        for (var i = 0, l = fn.length; i < l; i++)
            ns[i] = fn[i].jqote_id;

        return ns.length ?
            ns.sort().join('.').replace(/(\b\d+\b)\.(?:\1(\.|$))+/g, '$1$2') : false;
    }

    function lambda(tmpl, t) {
        var f, fn = [], t = t || tag,
            type = type_of.call(tmpl);

        if (type === FUNC)
            return tmpl.jqote_id ? [tmpl] : false;

        if (type !== ARR)
            return [$.jqotec(tmpl, t)];

        if (type === ARR)
            for (var i = 0, l = tmpl.length; i < l; i++)
                if (f = lambda(tmpl[i], t)) fn.push(f[0]);

        return fn.length ? fn : false;
    }

    $.fn.extend({
        jqote: function (data, t) {
            var data = type_of.call(data) === ARR ? data : [data],
                dom = '';

            this.each(function (i) {
                var fn = $.jqotec(this, t);

                for (var j = 0; j < data.length; j++)
                    dom += fn.call(data[j], i, j, data, fn);
            });

            return dom;
        }
    });

    $.each({ app: 'append', pre: 'prepend', sub: 'html' }, function (name, method) {
        $.fn['jqote' + name] = function (elem, data, t) {
            var ns, regexp, str = $.jqote(elem, data, t),
                $$ = !qreg.test(str) ?
                    function (str) { return $(document.createTextNode(str)); } : $;

            if (!!(ns = dotted_ns(lambda(elem))))
                regexp = new RegExp('(^|\\.)' + ns.split('.').join('\\.(.*)?') + '(\\.|$)');

            return this.each(function () {
                var dom = $$(str);

                $(this)[method](dom);

                (dom[0].nodeType === 3 ?
                    $(this) : dom).trigger('jqote.' + name, [dom, regexp]);
            });
        };
    });

    $.extend({
        jqote: function (elem, data, t) {
            var str = '', t = t || tag,
                fn = lambda(elem, t);

            if (fn === false)
                raise(new Error('Empty or undefined template passed to $.jqote'), { type: JQOTE2_TMPL_UNDEF_ERROR });

            data = type_of.call(data) !== ARR ?
                [data] : data;

            for (var i = 0, l = fn.length; i < l; i++)
                for (var j = 0; j < data.length; j++)
                    str += fn[i].call(data[j], i, j, data, fn[i]);

            return str;
        },

        jqotec: function (template, t) {
            var cache, elem, tmpl, t = t || tag,
                type = type_of.call(template);

            if (type === STR && qreg.test(template)) {
                elem = tmpl = template;

                if (cache = $.jqotecache[template]) return cache;
            } else {
                elem = type === STR || template.nodeType ?
                    $(template) : template instanceof jQuery ?
                    template : null;

                if (!elem[0] || !(tmpl = elem[0].innerHTML) && !(tmpl = elem.text()))
                    raise(new Error('Empty or undefined template passed to $.jqotec'), { type: JQOTE2_TMPL_UNDEF_ERROR });

                if (cache = $.jqotecache[$.data(elem[0], 'jqote_id')]) return cache;
            }

            var str = '', index,
                arr = tmpl.replace(/\s*<!\[CDATA\[\s*|\s*\]\]>\s*|[\r\n\t]/g, '')
                    .split('<' + t).join(t + '>\x1b')
                        .split(t + '>');

            for (var m = 0, l = arr.length; m < l; m++)
                str += arr[m].charAt(0) !== '\x1b' ?
                    "out+='" + arr[m].replace(/(\\|["'])/g, '\\$1') + "'" : (arr[m].charAt(1) === '=' ?
                        ';out+=(' + arr[m].substr(2) + ');' : (arr[m].charAt(1) === '!' ?
                            ';out+=$.jqotenc((' + arr[m].substr(2) + '));' : ';' + arr[m].substr(1)));

            str = 'try{' +
                ('var out="";' + str + ';return out;')
                    .split("out+='';").join('')
                        .split('var out="";out+=').join('var out=') +
                '}catch(e){e.type="' + JQOTE2_TMPL_EXEC_ERROR + '";e.args=arguments;e.template=arguments.callee.toString();throw e;}';

            try {
                var fn = new Function('i, j, data, fn', str);
            } catch (e) { raise(e, { type: JQOTE2_TMPL_COMP_ERROR }); }

            index = elem instanceof jQuery ?
                $.data(elem[0], 'jqote_id', n) : elem;

            return $.jqotecache[index] = (fn.jqote_id = n++, fn);
        },

        jqotefn: function (elem) {
            var type = type_of.call(elem),
                index = type === STR && qreg.test(elem) ?
                elem : $.data($(elem)[0], 'jqote_id');

            return $.jqotecache[index] || false;
        },

        jqotetag: function (str) {
            if (type_of.call(str) === STR) tag = str;
        },

        jqotenc: function (str) {
            return str.toString()
                    .replace(/&(?!\w+;)/g, '&#38;')
                        .split('<').join('&#60;').split('>').join('&#62;')
                            .split('"').join('&#34;').split("'").join('&#39;');
        },

        jqotecache: {}
    });

    $.event.special.jqote = {
        add: function (obj) {
            var ns, handler = obj.handler,
                data = !obj.data ?
                    [] : type_of.call(obj.data) !== ARR ?
                        [obj.data] : obj.data;

            if (!obj.namespace) obj.namespace = 'app.pre.sub';
            if (!data.length || !(ns = dotted_ns(lambda(data)))) return;

            obj.handler = function (event, dom, regexp) {
                return !regexp || regexp.test(ns) ?
                    handler.apply(this, [event, dom]) : null;
            };
        }
    };
})(jQuery);;
/*!
 * jQuery resize event - v1.1 - 3/14/2010
 * http://benalman.com/projects/jquery-resize-plugin/
 * 
 * Copyright (c) 2010 "Cowboy" Ben Alman
 * Dual licensed under the MIT and GPL licenses.
 * http://benalman.com/about/license/
 */

// Script: jQuery resize event
//
// *Version: 1.1, Last updated: 3/14/2010*
// 
// Project Home - http://benalman.com/projects/jquery-resize-plugin/
// GitHub       - http://github.com/cowboy/jquery-resize/
// Source       - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.js
// (Minified)   - http://github.com/cowboy/jquery-resize/raw/master/jquery.ba-resize.min.js (1.0kb)
// 
// About: License
// 
// Copyright (c) 2010 "Cowboy" Ben Alman,
// Dual licensed under the MIT and GPL licenses.
// http://benalman.com/about/license/
// 
// About: Examples
// 
// This working example, complete with fully commented code, illustrates a few
// ways in which this plugin can be used.
// 
// resize event - http://benalman.com/code/projects/jquery-resize/examples/resize/
// 
// About: Support and Testing
// 
// Information about what version or versions of jQuery this plugin has been
// tested with, what browsers it has been tested in, and where the unit tests
// reside (so you can test it yourself).
// 
// jQuery Versions - 1.3.2, 1.4.1, 1.4.2
// Browsers Tested - Internet Explorer 6-8, Firefox 2-3.6, Safari 3-4, Chrome, Opera 9.6-10.1.
// Unit Tests      - http://benalman.com/code/projects/jquery-resize/unit/
// 
// About: Release History
// 
// 1.1 - (3/14/2010) Fixed a minor bug that was causing the event to trigger
//       immediately after bind in some circumstances. Also changed $.fn.data
//       to $.data to improve performance.
// 1.0 - (2/10/2010) Initial release

(function ($, window, undefined) {
    '$:nomunge'; // Used by YUI compressor.

    // A jQuery object containing all non-window elements to which the resize
    // event is bound.
    var elems = $([]),

      // Extend $.resize if it already exists, otherwise create it.
      jq_resize = $.resize = $.extend($.resize, {}),

      timeout_id,

      // Reused strings.
      str_setTimeout = 'setTimeout',
      str_resize = 'resize',
      str_data = str_resize + '-special-event',
      str_delay = 'delay',
      str_throttle = 'throttleWindow';

    // Property: jQuery.resize.delay
    // 
    // The numeric interval (in milliseconds) at which the resize event polling
    // loop executes. Defaults to 250.

    jq_resize[str_delay] = 250;

    // Property: jQuery.resize.throttleWindow
    // 
    // Throttle the native window object resize event to fire no more than once
    // every <jQuery.resize.delay> milliseconds. Defaults to true.
    // 
    // Because the window object has its own resize event, it doesn't need to be
    // provided by this plugin, and its execution can be left entirely up to the
    // browser. However, since certain browsers fire the resize event continuously
    // while others do not, enabling this will throttle the window resize event,
    // making event behavior consistent across all elements in all browsers.
    // 
    // While setting this property to false will disable window object resize
    // event throttling, please note that this property must be changed before any
    // window object resize event callbacks are bound.

    jq_resize[str_throttle] = true;

    // Event: resize event
    // 
    // Fired when an element's width or height changes. Because browsers only
    // provide this event for the window element, for other elements a polling
    // loop is initialized, running every <jQuery.resize.delay> milliseconds
    // to see if elements' dimensions have changed. You may bind with either
    // .resize( fn ) or .bind( "resize", fn ), and unbind with .unbind( "resize" ).
    // 
    // Usage:
    // 
    // > jQuery('selector').bind( 'resize', function(e) {
    // >   // element's width or height has changed!
    // >   ...
    // > });
    // 
    // Additional Notes:
    // 
    // * The polling loop is not created until at least one callback is actually
    //   bound to the 'resize' event, and this single polling loop is shared
    //   across all elements.
    // 
    // Double firing issue in jQuery 1.3.2:
    // 
    // While this plugin works in jQuery 1.3.2, if an element's event callbacks
    // are manually triggered via .trigger( 'resize' ) or .resize() those
    // callbacks may double-fire, due to limitations in the jQuery 1.3.2 special
    // events system. This is not an issue when using jQuery 1.4+.
    // 
    // > // While this works in jQuery 1.4+
    // > $(elem).css({ width: new_w, height: new_h }).resize();
    // > 
    // > // In jQuery 1.3.2, you need to do this:
    // > var elem = $(elem);
    // > elem.css({ width: new_w, height: new_h });
    // > elem.data( 'resize-special-event', { width: elem.width(), height: elem.height() } );
    // > elem.resize();

    $.event.special[str_resize] = {

        // Called only when the first 'resize' event callback is bound per element.
        setup: function () {
            // Since window has its own native 'resize' event, return false so that
            // jQuery will bind the event using DOM methods. Since only 'window'
            // objects have a .setTimeout method, this should be a sufficient test.
            // Unless, of course, we're throttling the 'resize' event for window.
            if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; }

            var elem = $(this);

            // Add this element to the list of internal elements to monitor.
            elems = elems.add(elem);

            // Initialize data store on the element.
            $.data(this, str_data, { w: elem.width(), h: elem.height() });

            // If this is the first element added, start the polling loop.
            if (elems.length === 1) {
                loopy();
            }
        },

        // Called only when the last 'resize' event callback is unbound per element.
        teardown: function () {
            // Since window has its own native 'resize' event, return false so that
            // jQuery will unbind the event using DOM methods. Since only 'window'
            // objects have a .setTimeout method, this should be a sufficient test.
            // Unless, of course, we're throttling the 'resize' event for window.
            if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; }

            var elem = $(this);

            // Remove this element from the list of internal elements to monitor.
            elems = elems.not(elem);

            // Remove any data stored on the element.
            elem.removeData(str_data);

            // If this is the last element removed, stop the polling loop.
            if (!elems.length) {
                clearTimeout(timeout_id);
            }
        },

        // Called every time a 'resize' event callback is bound per element (new in
        // jQuery 1.4).
        add: function (handleObj) {
            // Since window has its own native 'resize' event, return false so that
            // jQuery doesn't modify the event object. Unless, of course, we're
            // throttling the 'resize' event for window.
            if (!jq_resize[str_throttle] && this[str_setTimeout]) { return false; }

            var old_handler;

            // The new_handler function is executed every time the event is triggered.
            // This is used to update the internal element data store with the width
            // and height when the event is triggered manually, to avoid double-firing
            // of the event callback. See the "Double firing issue in jQuery 1.3.2"
            // comments above for more information.

            function new_handler(e, w, h) {
                var elem = $(this),
                  data = $.data(this, str_data);

                // If called from the polling loop, w and h will be passed in as
                // arguments. If called manually, via .trigger( 'resize' ) or .resize(),
                // those values will need to be computed.
                data.w = w !== undefined ? w : elem.width();
                data.h = h !== undefined ? h : elem.height();

                old_handler.apply(this, arguments);
            };

            // This may seem a little complicated, but it normalizes the special event
            // .add method between jQuery 1.4/1.4.1 and 1.4.2+
            if ($.isFunction(handleObj)) {
                // 1.4, 1.4.1
                old_handler = handleObj;
                return new_handler;
            } else {
                // 1.4.2+
                old_handler = handleObj.handler;
                handleObj.handler = new_handler;
            }
        }

    };

    function loopy() {

        // Start the polling loop, asynchronously.
        timeout_id = window[str_setTimeout](function () {

            // Iterate over all elements to which the 'resize' event is bound.
            elems.each(function () {
                var elem = $(this),
                  width = elem.width(),
                  height = elem.height(),
                  data = $.data(this, str_data);

                // If element size has changed since the last time, update the element
                // data store and trigger the 'resize' event.
                if (width !== data.w || height !== data.h) {
                    elem.trigger(str_resize, [data.w = width, data.h = height]);
                }

            });

            // Loop.
            loopy();

        }, jq_resize[str_delay]);

    };

})(jQuery, this);;
"use strict";var sjcl={cipher:{},hash:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}};
sjcl.cipher.aes=function(a){this.h[0][0][0]||this.z();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^
g[3][f[c&255]]}};
sjcl.cipher.aes.prototype={encrypt:function(a){return this.I(a,0)},decrypt:function(a){return this.I(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],z:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e=
0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},I:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j<k;j++){g=n[d>>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16&
255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}};
sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},concat:function(a,b){if(a.length===0||b.length===0)return a.concat(b);var c=a[a.length-1],d=sjcl.bitArray.getPartial(c);return d===32?a.concat(b):sjcl.bitArray.P(b,d,c|0,a.slice(0,a.length-1))},bitLength:function(a){var b=a.length;if(b===0)return 0;return(b-1)*32+sjcl.bitArray.getPartial(a[b-1])},clamp:function(a,b){if(a.length*32<b)return a;a=a.slice(0,Math.ceil(b/
32));var c=a.length;b&=31;if(c>0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d<a.length;d++)c|=a[d]^b[d];return c===0},P:function(a,b,c,d){var e;e=0;if(d===undefined)d=[];for(;b>=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);
for(e=0;e<a.length;e++){d.push(c|a[e]>>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}};
sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d<c/8;d++){if((d&3)===0)e=a[d/4];b+=String.fromCharCode(e>>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c<a.length;c++){d=d<<8|a.charCodeAt(c);if((c&3)===3){b.push(d);d=0}}c&3&&b.push(sjcl.bitArray.partial(8*(c&3),d));return b}};
sjcl.codec.hex={fromBits:function(a){var b="",c;for(c=0;c<a.length;c++)b+=((a[c]|0)+0xf00000000000).toString(16).substr(4);return b.substr(0,sjcl.bitArray.bitLength(a)/4)},toBits:function(a){var b,c=[],d;a=a.replace(/\s|0x/g,"");d=a.length;a+="00000000";for(b=0;b<a.length;b+=8)c.push(parseInt(a.substr(b,8),16)^0);return sjcl.bitArray.clamp(c,d*4)}};
sjcl.codec.base64={F:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",fromBits:function(a,b){var c="",d,e=0,f=sjcl.codec.base64.F,g=0,h=sjcl.bitArray.bitLength(a);for(d=0;c.length*6<h;){c+=f.charAt((g^a[d]>>>e)>>>26);if(e<6){g=a[d]<<6-e;e+=26;d++}else{g<<=6;e-=6}}for(;c.length&3&&!b;)c+="=";return c},toBits:function(a){a=a.replace(/\s|=/g,"");var b=[],c,d=0,e=sjcl.codec.base64.F,f=0,g;for(c=0;c<a.length;c++){g=e.indexOf(a.charAt(c));if(g<0)throw new sjcl.exception.invalid("this isn't base64!");
if(d>26){d-=26;b.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&b.push(sjcl.bitArray.partial(d&56,f,1));return b}};sjcl.hash.sha256=function(a){this.a[0]||this.z();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()};
sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.D(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/
4294967296));for(b.push(this.e|0);b.length;)this.D(b.splice(0,16));this.reset();return c},N:[],a:[],z:function(){function a(e){return(e-Math.floor(e))*0x100000000|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},D:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^
b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}};
sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.H(a,b,c,d,e,f);g=sjcl.mode.ccm.J(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b,
h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.J(a,i,c,k,e,b);a=sjcl.mode.ccm.H(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},H:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d<g.length;d+=4)f=a.encrypt(i(f,g.slice(d,d+4).concat([0,0,0])))}for(d=0;d<b.length;d+=4)f=a.encrypt(i(f,b.slice(d,d+4).concat([0,0,0])));return h.clamp(f,e*8)},J:function(a,b,c,d,e,f){var g,h=sjcl.bitArray;g=h.k;var i=b.length,k=h.bitLength(b);c=h.concat([h.partial(8,
f-1)],c).concat([0,0,0]).slice(0,4);d=h.bitSlice(g(d,a.encrypt(c)),0,e);if(!i)return{tag:d,data:[]};for(g=0;g<i;g+=4){c[3]++;e=a.encrypt(c);b[g]^=e[0];b[g+1]^=e[1];b[g+2]^=e[2];b[g+3]^=e[3]}return{tag:d,data:h.clamp(b,k)}}};
sjcl.mode.ocb2={name:"ocb2",encrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");var g,h=sjcl.mode.ocb2.B,i=sjcl.bitArray,k=i.k,j=[0,0,0,0];c=h(a.encrypt(c));var l,m=[];d=d||[];e=e||64;for(g=0;g+4<b.length;g+=4){l=b.slice(g,g+4);j=k(j,l);m=m.concat(k(c,a.encrypt(k(c,l))));c=h(c)}l=b.slice(g);b=i.bitLength(l);g=a.encrypt(k(c,[0,0,0,b]));l=i.clamp(k(l.concat([0,0,0]),g),b);j=k(j,k(l.concat([0,0,0]),g));j=a.encrypt(k(j,k(c,h(c))));
if(d.length)j=k(j,f?d:sjcl.mode.ocb2.pmac(a,d));return m.concat(i.concat(l,i.clamp(j,e)))},decrypt:function(a,b,c,d,e,f){if(sjcl.bitArray.bitLength(c)!==128)throw new sjcl.exception.invalid("ocb iv must be 128 bits");e=e||64;var g=sjcl.mode.ocb2.B,h=sjcl.bitArray,i=h.k,k=[0,0,0,0],j=g(a.encrypt(c)),l,m,n=sjcl.bitArray.bitLength(b)-e,o=[];d=d||[];for(c=0;c+4<n/32;c+=4){l=i(j,a.decrypt(i(j,b.slice(c,c+4))));k=i(k,l);o=o.concat(l);j=g(j)}m=n-c*32;l=a.encrypt(i(j,[0,0,0,m]));l=i(l,h.clamp(b.slice(c),
m).concat([0,0,0]));k=i(k,l);k=a.encrypt(i(k,i(j,g(j))));if(d.length)k=i(k,f?d:sjcl.mode.ocb2.pmac(a,d));if(!h.equal(h.clamp(k,e),h.bitSlice(b,n)))throw new sjcl.exception.corrupt("ocb: tag doesn't match");return o.concat(h.clamp(l,m))},pmac:function(a,b){var c,d=sjcl.mode.ocb2.B,e=sjcl.bitArray,f=e.k,g=[0,0,0,0],h=a.encrypt([0,0,0,0]);h=f(h,d(d(h)));for(c=0;c+4<b.length;c+=4){h=d(h);g=f(g,a.encrypt(f(h,b.slice(c,c+4))))}b=b.slice(c);if(e.bitLength(b)<128){h=f(h,d(h));b=e.concat(b,[2147483648|0,0,
0,0])}g=f(g,b);return a.encrypt(f(d(f(h,d(h))),g))},B:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b<d;b++){c[0][b]=a[b]^909522486;c[1][b]=a[b]^1549556828}this.l[0].update(c[0]);this.l[1].update(c[1])};
sjcl.misc.hmac.prototype.encrypt=sjcl.misc.hmac.prototype.mac=function(a){a=(new this.M(this.l[0])).update(a).finalize();return(new this.M(this.l[1])).update(a).finalize()};
sjcl.misc.pbkdf2=function(a,b,c,d,e){c=c||1E3;if(d<0||c<0)throw sjcl.exception.invalid("invalid params to pbkdf2");if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,i,k=[],j=sjcl.bitArray;for(i=1;32*k.length<(d||1);i++){e=f=a.encrypt(j.concat(b,[i]));for(g=1;g<c;g++){f=a.encrypt(f);for(h=0;h<f.length;h++)e[h]^=f[h]}k=k.concat(e)}if(d)k=j.clamp(k,d);return k};
sjcl.random={randomWords:function(a,b){var c=[];b=this.isReady(b);var d;if(b===0)throw new sjcl.exception.notReady("generator isn't seeded");else b&2&&this.U(!(b&1));for(b=0;b<a;b+=4){(b+1)%0x10000===0&&this.L();d=this.w();c.push(d[0],d[1],d[2],d[3])}this.L();return c.slice(0,a)},setDefaultParanoia:function(a){this.t=a},addEntropy:function(a,b,c){c=c||"user";var d,e,f=(new Date).valueOf(),g=this.q[c],h=this.isReady(),i=0;d=this.G[c];if(d===undefined)d=this.G[c]=this.R++;if(g===undefined)g=this.q[c]=
0;this.q[c]=(this.q[c]+1)%this.b.length;switch(typeof a){case "number":if(b===undefined)b=1;this.b[g].update([d,this.u++,1,b,f,1,a|0]);break;case "object":c=Object.prototype.toString.call(a);if(c==="[object Uint32Array]"){e=[];for(c=0;c<a.length;c++)e.push(a[c]);a=e}else{if(c!=="[object Array]")i=1;for(c=0;c<a.length&&!i;c++)if(typeof a[c]!="number")i=1}if(!i){if(b===undefined)for(c=b=0;c<a.length;c++)for(e=a[c];e>0;){b++;e>>>=1}this.b[g].update([d,this.u++,2,b,f,a.length].concat(a))}break;case "string":if(b===
undefined)b=a.length;this.b[g].update([d,this.u++,3,b,f,a.length]);this.b[g].update(a);break;default:i=1}if(i)throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g,this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.C[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=
this.C[a?a:this.t];return this.g>=a?1:this.f>a?1:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload",this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",
this.o,false);window.removeEventListener("mousemove",this.p,false)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c;a=this.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b<d.length;b++){c=d[b];delete a[c]}},b:[new sjcl.hash.sha256],j:[0],A:0,q:{},u:0,G:{},R:0,g:0,f:0,O:0,a:[0,0,0,0,0,0,0,0],d:[0,0,0,0],s:undefined,
t:6,m:false,r:{progress:{},seeded:{}},Q:0,C:[0,48,64,96,128,192,0x100,384,512,768,1024],w:function(){for(var a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}return this.s.encrypt(this.d)},L:function(){this.a=this.w().concat(this.w());this.s=new sjcl.cipher.aes(this.a)},T:function(a){this.a=sjcl.hash.sha256.hash(this.a.concat(a));this.s=new sjcl.cipher.aes(this.a);for(a=0;a<4;a++){this.d[a]=this.d[a]+1|0;if(this.d[a])break}},U:function(a){var b=[],c=0,d;this.O=b[0]=(new Date).valueOf()+3E4;for(d=
0;d<16;d++)b.push(Math.random()*0x100000000|0);for(d=0;d<this.b.length;d++){b=b.concat(this.b[d].finalize());c+=this.j[d];this.j[d]=0;if(!a&&this.A&1<<d)break}if(this.A>=1<<this.b.length){this.b.push(new sjcl.hash.sha256);this.j.push(0)}this.f-=c;if(c>this.g)this.g=c;this.A++;this.T(b)},p:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX||0,a.y||a.clientY||a.offsetY||0],2,"mouse")},o:function(){sjcl.random.addEntropy((new Date).valueOf(),2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];
var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c<d.length;c++)d[c](b)}};try{var s=new Uint32Array(32);crypto.getRandomValues(s);sjcl.random.addEntropy(s,1024,"crypto['getRandomValues']")}catch(t){}
sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},encrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.c({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.c(f,c);c=f.adata;if(typeof f.salt==="string")f.salt=sjcl.codec.base64.toBits(f.salt);if(typeof f.iv==="string")f.iv=sjcl.codec.base64.toBits(f.iv);if(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||typeof a==="string"&&f.iter<=100||f.ts!==64&&f.ts!==96&&f.ts!==128||f.ks!==128&&f.ks!==192&&f.ks!==0x100||f.iv.length<
2||f.iv.length>4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){g=sjcl.misc.cachedPbkdf2(a,f);a=g.key.slice(0,f.ks/32);f.salt=g.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);if(typeof c==="string")c=sjcl.codec.utf8String.toBits(c);g=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return e.encode(f)},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),e.decode(b)),
c,true);var f;c=b.adata;if(typeof b.salt==="string")b.salt=sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){f=sjcl.misc.cachedPbkdf2(a,b);a=f.key.slice(0,b.ks/32);b.salt=f.salt}if(typeof c===
"string")c=sjcl.codec.utf8String.toBits(c);f=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(f,b.ct,b.iv,c,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+'"'+b+'":';d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+
sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type");}}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c<a.length;c++){if(!(d=a[c].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i)))throw new sjcl.exception.invalid("json decode: this isn't json!");b[d[2]]=
d[3]?parseInt(d[3],10):d[2].match(/^(ct|salt|iv)$/)?sjcl.codec.base64.toBits(d[4]):unescape(d[4])}return b},c:function(a,b,c){if(a===undefined)a={};if(b===undefined)return a;var d;for(d in b)if(b.hasOwnProperty(d)){if(c&&a[d]!==undefined&&a[d]!==b[d])throw new sjcl.exception.invalid("required parameter overridden");a[d]=b[d]}return a},V:function(a,b){var c={},d;for(d=0;d<b.length;d++)if(a[b[d]]!==undefined)c[b[d]]=a[b[d]];return c}};sjcl.encrypt=sjcl.json.encrypt;sjcl.decrypt=sjcl.json.decrypt;
sjcl.misc.S={};sjcl.misc.cachedPbkdf2=function(a,b){var c=sjcl.misc.S,d;b=b||{};d=b.iter||1E3;c=c[a]=c[a]||{};d=c[d]=c[d]||{firstSalt:b.salt&&b.salt.length?b.salt.slice(0):sjcl.random.randomWords(2,0)};c=b.salt===undefined?d.firstSalt:b.salt;d[c]=d[c]||sjcl.misc.pbkdf2(a,c,b.iter);return{key:d[c].slice(0),salt:c.slice(0)}};;
/**
 * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
 *
 * @version 0.6.11
 * @codingstandard ftlabs-jsv2
 * @copyright The Financial Times Limited [All Rights Reserved]
 * @license MIT License (see LICENSE.txt)
 */

/*jslint browser:true, node:true*/
/*global define, Event, Node*/


/**
 * Instantiate fast-clicking listeners on the specificed layer.
 *
 * @constructor
 * @param {Element} layer The layer to listen on
 */
function FastClick(layer) {
	'use strict';
	var oldOnClick, self = this;


	/**
	 * Whether a click is currently being tracked.
	 *
	 * @type boolean
	 */
	this.trackingClick = false;


	/**
	 * Timestamp for when when click tracking started.
	 *
	 * @type number
	 */
	this.trackingClickStart = 0;


	/**
	 * The element being tracked for a click.
	 *
	 * @type EventTarget
	 */
	this.targetElement = null;


	/**
	 * X-coordinate of touch start event.
	 *
	 * @type number
	 */
	this.touchStartX = 0;


	/**
	 * Y-coordinate of touch start event.
	 *
	 * @type number
	 */
	this.touchStartY = 0;


	/**
	 * ID of the last touch, retrieved from Touch.identifier.
	 *
	 * @type number
	 */
	this.lastTouchIdentifier = 0;


	/**
	 * Touchmove boundary, beyond which a click will be cancelled.
	 *
	 * @type number
	 */
	this.touchBoundary = 10;


	/**
	 * The FastClick layer.
	 *
	 * @type Element
	 */
	this.layer = layer;

	if (!layer || !layer.nodeType) {
		throw new TypeError('Layer must be a document node');
	}

	/** @type function() */
	this.onClick = function() { return FastClick.prototype.onClick.apply(self, arguments); };

	/** @type function() */
	this.onMouse = function() { return FastClick.prototype.onMouse.apply(self, arguments); };

	/** @type function() */
	this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); };

	/** @type function() */
	this.onTouchMove = function() { return FastClick.prototype.onTouchMove.apply(self, arguments); };

	/** @type function() */
	this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); };

	/** @type function() */
	this.onTouchCancel = function() { return FastClick.prototype.onTouchCancel.apply(self, arguments); };

	if (FastClick.notNeeded(layer)) {
		return;
	}

	// Set up event handlers as required
	if (this.deviceIsAndroid) {
		layer.addEventListener('mouseover', this.onMouse, true);
		layer.addEventListener('mousedown', this.onMouse, true);
		layer.addEventListener('mouseup', this.onMouse, true);
	}

	layer.addEventListener('click', this.onClick, true);
	layer.addEventListener('touchstart', this.onTouchStart, false);
	layer.addEventListener('touchmove', this.onTouchMove, false);
	layer.addEventListener('touchend', this.onTouchEnd, false);
	layer.addEventListener('touchcancel', this.onTouchCancel, false);

	// Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
	// which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
	// layer when they are cancelled.
	if (!Event.prototype.stopImmediatePropagation) {
		layer.removeEventListener = function(type, callback, capture) {
			var rmv = Node.prototype.removeEventListener;
			if (type === 'click') {
				rmv.call(layer, type, callback.hijacked || callback, capture);
			} else {
				rmv.call(layer, type, callback, capture);
			}
		};

		layer.addEventListener = function(type, callback, capture) {
			var adv = Node.prototype.addEventListener;
			if (type === 'click') {
				adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
					if (!event.propagationStopped) {
						callback(event);
					}
				}), capture);
			} else {
				adv.call(layer, type, callback, capture);
			}
		};
	}

	// If a handler is already declared in the element's onclick attribute, it will be fired before
	// FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
	// adding it as listener.
	if (typeof layer.onclick === 'function') {

		// Android browser on at least 3.2 requires a new reference to the function in layer.onclick
		// - the old one won't work if passed to addEventListener directly.
		oldOnClick = layer.onclick;
		layer.addEventListener('click', function(event) {
			oldOnClick(event);
		}, false);
		layer.onclick = null;
	}
}


/**
 * Android requires exceptions.
 *
 * @type boolean
 */
FastClick.prototype.deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0;


/**
 * iOS requires exceptions.
 *
 * @type boolean
 */
FastClick.prototype.deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent);


/**
 * iOS 4 requires an exception for select elements.
 *
 * @type boolean
 */
FastClick.prototype.deviceIsIOS4 = FastClick.prototype.deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);


/**
 * iOS 6.0(+?) requires the target element to be manually derived
 *
 * @type boolean
 */
FastClick.prototype.deviceIsIOSWithBadTarget = FastClick.prototype.deviceIsIOS && (/OS ([6-9]|\d{2})_\d/).test(navigator.userAgent);


/**
 * Determine whether a given element requires a native click.
 *
 * @param {EventTarget|Element} target Target DOM element
 * @returns {boolean} Returns true if the element needs a native click
 */
FastClick.prototype.needsClick = function(target) {
	'use strict';
	switch (target.nodeName.toLowerCase()) {

	// Don't send a synthetic click to disabled inputs (issue #62)
	case 'button':
	case 'select':
	case 'textarea':
		if (target.disabled) {
			return true;
		}

		break;
	case 'input':

		// File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
		if ((this.deviceIsIOS && target.type === 'file') || target.disabled) {
			return true;
		}

		break;
	case 'label':
	case 'video':
		return true;
	}

	return (/\bneedsclick\b/).test(target.className);
};


/**
 * Determine whether a given element requires a call to focus to simulate click into element.
 *
 * @param {EventTarget|Element} target Target DOM element
 * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
 */
FastClick.prototype.needsFocus = function(target) {
	'use strict';
	switch (target.nodeName.toLowerCase()) {
	case 'textarea':
		return true;
	case 'select':
		return !this.deviceIsAndroid;
	case 'input':
		switch (target.type) {
		case 'button':
		case 'checkbox':
		case 'file':
		case 'image':
		case 'radio':
		case 'submit':
			return false;
		}

		// No point in attempting to focus disabled inputs
		return !target.disabled && !target.readOnly;
	default:
		return (/\bneedsfocus\b/).test(target.className);
	}
};


/**
 * Send a click event to the specified element.
 *
 * @param {EventTarget|Element} targetElement
 * @param {Event} event
 */
FastClick.prototype.sendClick = function(targetElement, event) {
	'use strict';
	var clickEvent, touch;

	// On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
	if (document.activeElement && document.activeElement !== targetElement) {
		document.activeElement.blur();
	}

	touch = event.changedTouches[0];

	// Synthesise a click event, with an extra attribute so it can be tracked
	clickEvent = document.createEvent('MouseEvents');
	clickEvent.initMouseEvent(this.determineEventType(targetElement), true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
	clickEvent.forwardedTouchEvent = true;
	targetElement.dispatchEvent(clickEvent);
};

FastClick.prototype.determineEventType = function(targetElement) {
	'use strict';

	//Issue #159: Android Chrome Select Box does not open with a synthetic click event
	if (this.deviceIsAndroid && targetElement.tagName.toLowerCase() === 'select') {
		return 'mousedown';
	}

	return 'click';
};


/**
 * @param {EventTarget|Element} targetElement
 */
FastClick.prototype.focus = function(targetElement) {
	'use strict';
	var length;

	// Issue #160: on iOS 7, some input elements (e.g. date datetime) throw a vague TypeError on setSelectionRange. These elements don't have an integer value for the selectionStart and selectionEnd properties, but unfortunately that can't be used for detection because accessing the properties also throws a TypeError. Just check the type instead. Filed as Apple bug #15122724.
	if (this.deviceIsIOS && targetElement.setSelectionRange && targetElement.type.indexOf('date') !== 0 && targetElement.type !== 'time') {
		length = targetElement.value.length;
		targetElement.setSelectionRange(length, length);
	} else {
		targetElement.focus();
	}
};


/**
 * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
 *
 * @param {EventTarget|Element} targetElement
 */
FastClick.prototype.updateScrollParent = function(targetElement) {
	'use strict';
	var scrollParent, parentElement;

	scrollParent = targetElement.fastClickScrollParent;

	// Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
	// target element was moved to another parent.
	if (!scrollParent || !scrollParent.contains(targetElement)) {
		parentElement = targetElement;
		do {
			if (parentElement.scrollHeight > parentElement.offsetHeight) {
				scrollParent = parentElement;
				targetElement.fastClickScrollParent = parentElement;
				break;
			}

			parentElement = parentElement.parentElement;
		} while (parentElement);
	}

	// Always update the scroll top tracker if possible.
	if (scrollParent) {
		scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
	}
};


/**
 * @param {EventTarget} targetElement
 * @returns {Element|EventTarget}
 */
FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
	'use strict';

	// On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
	if (eventTarget.nodeType === Node.TEXT_NODE) {
		return eventTarget.parentNode;
	}

	return eventTarget;
};


/**
 * On touch start, record the position and scroll offset.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onTouchStart = function(event) {
	'use strict';
	var targetElement, touch, selection;

	// Ignore multiple touches, otherwise pinch-to-zoom is prevented if both fingers are on the FastClick element (issue #111).
	if (event.targetTouches.length > 1) {
		return true;
	}

	targetElement = this.getTargetElementFromEventTarget(event.target);
	touch = event.targetTouches[0];

	if (this.deviceIsIOS) {

		// Only trusted events will deselect text on iOS (issue #49)
		selection = window.getSelection();
		if (selection.rangeCount && !selection.isCollapsed) {
			return true;
		}

		if (!this.deviceIsIOS4) {

			// Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
			// when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
			// with the same identifier as the touch event that previously triggered the click that triggered the alert.
			// Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
			// immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
			if (touch.identifier === this.lastTouchIdentifier) {
				event.preventDefault();
				return false;
			}

			this.lastTouchIdentifier = touch.identifier;

			// If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
			// 1) the user does a fling scroll on the scrollable layer
			// 2) the user stops the fling scroll with another tap
			// then the event.target of the last 'touchend' event will be the element that was under the user's finger
			// when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
			// is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
			this.updateScrollParent(targetElement);
		}
	}

	this.trackingClick = true;
	this.trackingClickStart = event.timeStamp;
	this.targetElement = targetElement;

	this.touchStartX = touch.pageX;
	this.touchStartY = touch.pageY;

	// Prevent phantom clicks on fast double-tap (issue #36)
	if ((event.timeStamp - this.lastClickTime) < 200) {
		event.preventDefault();
	}

	return true;
};


/**
 * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.touchHasMoved = function(event) {
	'use strict';
	var touch = event.changedTouches[0], boundary = this.touchBoundary;

	if (Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary) {
		return true;
	}

	return false;
};


/**
 * Update the last position.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onTouchMove = function(event) {
	'use strict';
	if (!this.trackingClick) {
		return true;
	}

	// If the touch has moved, cancel the click tracking
	if (this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) {
		this.trackingClick = false;
		this.targetElement = null;
	}

	return true;
};


/**
 * Attempt to find the labelled control for the given label element.
 *
 * @param {EventTarget|HTMLLabelElement} labelElement
 * @returns {Element|null}
 */
FastClick.prototype.findControl = function(labelElement) {
	'use strict';

	// Fast path for newer browsers supporting the HTML5 control attribute
	if (labelElement.control !== undefined) {
		return labelElement.control;
	}

	// All browsers under test that support touch events also support the HTML5 htmlFor attribute
	if (labelElement.htmlFor) {
		return document.getElementById(labelElement.htmlFor);
	}

	// If no for attribute exists, attempt to retrieve the first labellable descendant element
	// the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
	return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
};


/**
 * On touch end, determine whether to send a click event at once.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onTouchEnd = function(event) {
	'use strict';
	var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;

	if (!this.trackingClick) {
		return true;
	}

	// Prevent phantom clicks on fast double-tap (issue #36)
	if ((event.timeStamp - this.lastClickTime) < 200) {
		this.cancelNextClick = true;
		return true;
	}

	// Reset to prevent wrong click cancel on input (issue #156).
	this.cancelNextClick = false;

	this.lastClickTime = event.timeStamp;

	trackingClickStart = this.trackingClickStart;
	this.trackingClick = false;
	this.trackingClickStart = 0;

	// On some iOS devices, the targetElement supplied with the event is invalid if the layer
	// is performing a transition or scroll, and has to be re-detected manually. Note that
	// for this to function correctly, it must be called *after* the event target is checked!
	// See issue #57; also filed as rdar://13048589 .
	if (this.deviceIsIOSWithBadTarget) {
		touch = event.changedTouches[0];

		// In certain cases arguments of elementFromPoint can be negative, so prevent setting targetElement to null
		targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement;
		targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent;
	}

	targetTagName = targetElement.tagName.toLowerCase();
	if (targetTagName === 'label') {
		forElement = this.findControl(targetElement);
		if (forElement) {
			this.focus(targetElement);
			if (this.deviceIsAndroid) {
				return false;
			}

			targetElement = forElement;
		}
	} else if (this.needsFocus(targetElement)) {

		// Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
		// Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
		if ((event.timeStamp - trackingClickStart) > 100 || (this.deviceIsIOS && window.top !== window && targetTagName === 'input')) {
			this.targetElement = null;
			return false;
		}

		this.focus(targetElement);

		// Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
		if (!this.deviceIsIOS4 || targetTagName !== 'select') {
			this.targetElement = null;
			event.preventDefault();
		}

		return false;
	}

	if (this.deviceIsIOS && !this.deviceIsIOS4) {

		// Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
		// and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
		scrollParent = targetElement.fastClickScrollParent;
		if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
			return true;
		}
	}

	// Prevent the actual click from going though - unless the target node is marked as requiring
	// real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
	if (!this.needsClick(targetElement)) {
		event.preventDefault();
		this.sendClick(targetElement, event);
	}

	return false;
};


/**
 * On touch cancel, stop tracking the click.
 *
 * @returns {void}
 */
FastClick.prototype.onTouchCancel = function() {
	'use strict';
	this.trackingClick = false;
	this.targetElement = null;
};


/**
 * Determine mouse events which should be permitted.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onMouse = function(event) {
	'use strict';

	// If a target element was never set (because a touch event was never fired) allow the event
	if (!this.targetElement) {
		return true;
	}

	if (event.forwardedTouchEvent) {
		return true;
	}

	// Programmatically generated events targeting a specific element should be permitted
	if (!event.cancelable) {
		return true;
	}

	// Derive and check the target element to see whether the mouse event needs to be permitted;
	// unless explicitly enabled, prevent non-touch click events from triggering actions,
	// to prevent ghost/doubleclicks.
	if (!this.needsClick(this.targetElement) || this.cancelNextClick) {

		// Prevent any user-added listeners declared on FastClick element from being fired.
		if (event.stopImmediatePropagation) {
			event.stopImmediatePropagation();
		} else {

			// Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
			event.propagationStopped = true;
		}

		// Cancel the event
		event.stopPropagation();
		event.preventDefault();

		return false;
	}

	// If the mouse event is permitted, return true for the action to go through.
	return true;
};


/**
 * On actual clicks, determine whether this is a touch-generated click, a click action occurring
 * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
 * an actual click which should be permitted.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onClick = function(event) {
	'use strict';
	var permitted;

	// It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
	if (this.trackingClick) {
		this.targetElement = null;
		this.trackingClick = false;
		return true;
	}

	// Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
	if (event.target.type === 'submit' && event.detail === 0) {
		return true;
	}

	permitted = this.onMouse(event);

	// Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
	if (!permitted) {
		this.targetElement = null;
	}

	// If clicks are permitted, return true for the action to go through.
	return permitted;
};


/**
 * Remove all FastClick's event listeners.
 *
 * @returns {void}
 */
FastClick.prototype.destroy = function() {
	'use strict';
	var layer = this.layer;

	if (this.deviceIsAndroid) {
		layer.removeEventListener('mouseover', this.onMouse, true);
		layer.removeEventListener('mousedown', this.onMouse, true);
		layer.removeEventListener('mouseup', this.onMouse, true);
	}

	layer.removeEventListener('click', this.onClick, true);
	layer.removeEventListener('touchstart', this.onTouchStart, false);
	layer.removeEventListener('touchmove', this.onTouchMove, false);
	layer.removeEventListener('touchend', this.onTouchEnd, false);
	layer.removeEventListener('touchcancel', this.onTouchCancel, false);
};


/**
 * Check whether FastClick is needed.
 *
 * @param {Element} layer The layer to listen on
 */
FastClick.notNeeded = function(layer) {
	'use strict';
	var metaViewport;
	var chromeVersion;

	// Devices that don't support touch don't need FastClick
	if (typeof window.ontouchstart === 'undefined') {
		return true;
	}

	// Chrome version - zero for other browsers
	chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [,0])[1];

	if (chromeVersion) {

		if (FastClick.prototype.deviceIsAndroid) {
			metaViewport = document.querySelector('meta[name=viewport]');
			
			if (metaViewport) {
				// Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
				if (metaViewport.content.indexOf('user-scalable=no') !== -1) {
					return true;
				}
				// Chrome 32 and above with width=device-width or less don't need FastClick
				if (chromeVersion > 31 && window.innerWidth <= window.screen.width) {
					return true;
				}
			}

		// Chrome desktop doesn't need FastClick (issue #15)
		} else {
			return true;
		}
	}

	// IE10 with -ms-touch-action: none, which disables double-tap-to-zoom (issue #97)
	if (layer.style.msTouchAction === 'none') {
		return true;
	}

	return false;
};


/**
 * Factory method for creating a FastClick object
 *
 * @param {Element} layer The layer to listen on
 */
FastClick.attach = function(layer) {
	'use strict';
	return new FastClick(layer);
};


if (typeof define !== 'undefined' && define.amd) {

	// AMD. Register as an anonymous module.
	define(function() {
		'use strict';
		return FastClick;
	});
} else if (typeof module !== 'undefined' && module.exports) {
	module.exports = FastClick.attach;
	module.exports.FastClick = FastClick;
} else {
	window.FastClick = FastClick;
}
;
/* mousetrap v1.6.1 craig.is/killing/mice */
(function(r,v,f){function w(a,b,g){a.addEventListener?a.addEventListener(b,g,!1):a.attachEvent("on"+b,g)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return p[a.which]?p[a.which]:t[a.which]?t[a.which]:String.fromCharCode(a.which).toLowerCase()}function F(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function x(a){return"shift"==a||"ctrl"==a||"alt"==a||
    "meta"==a}function B(a,b){var g,c,d,f=[];g=a;"+"===g?g=["+"]:(g=g.replace(/\+{2}/g,"+plus"),g=g.split("+"));for(d=0;d<g.length;++d)c=g[d],C[c]&&(c=C[c]),b&&"keypress"!=b&&D[c]&&(c=D[c],f.push("shift")),x(c)&&f.push(c);g=c;d=b;if(!d){if(!n){n={};for(var q in p)95<q&&112>q||p.hasOwnProperty(q)&&(n[p[q]]=q)}d=n[g]?"keydown":"keypress"}"keypress"==d&&f.length&&(d="keydown");return{key:c,modifiers:f,action:d}}function E(a,b){return null===a||a===v?!1:a===b?!0:E(a.parentNode,b)}function c(a){function b(a){a=
    a||{};var b=!1,l;for(l in n)a[l]?b=!0:n[l]=0;b||(y=!1)}function g(a,b,u,e,c,g){var l,m,k=[],f=u.type;if(!h._callbacks[a])return[];"keyup"==f&&x(a)&&(b=[a]);for(l=0;l<h._callbacks[a].length;++l)if(m=h._callbacks[a][l],(e||!m.seq||n[m.seq]==m.level)&&f==m.action){var d;(d="keypress"==f&&!u.metaKey&&!u.ctrlKey)||(d=m.modifiers,d=b.sort().join(",")===d.sort().join(","));d&&(d=e&&m.seq==e&&m.level==g,(!e&&m.combo==c||d)&&h._callbacks[a].splice(l,1),k.push(m))}return k}function f(a,b,c,e){h.stopCallback(b,
    b.target||b.srcElement,c,e)||!1!==a(b,c)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function d(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=A(a);b&&("keyup"==a.type&&z===b?z=!1:h.handleKey(b,F(a),a))}function p(a,c,u,e){function l(c){return function(){y=c;++n[a];clearTimeout(r);r=setTimeout(b,1E3)}}function g(c){f(u,c,a);"keyup"!==e&&(z=A(c));setTimeout(b,10)}for(var d=n[a]=0;d<c.length;++d){var m=d+1===c.length?g:l(e||
    B(c[d+1]).action);q(c[d],m,e,a,d)}}function q(a,b,c,e,d){h._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1<f.length?p(a,f,b,c):(c=B(a,c),h._callbacks[c.key]=h._callbacks[c.key]||[],g(c.key,c.modifiers,{type:c.action},e,a,d),h._callbacks[c.key][e?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:e,level:d,combo:a}))}var h=this;a=a||v;if(!(h instanceof c))return new c(a);h.target=a;h._callbacks={};h._directMap={};var n={},r,z=!1,t=!1,y=!1;h._handleKey=function(a,
    c,d){var e=g(a,c,d),k;c={};var h=0,l=!1;for(k=0;k<e.length;++k)e[k].seq&&(h=Math.max(h,e[k].level));for(k=0;k<e.length;++k)e[k].seq?e[k].level==h&&(l=!0,c[e[k].seq]=1,f(e[k].callback,d,e[k].combo,e[k].seq)):l||f(e[k].callback,d,e[k].combo);e="keypress"==d.type&&t;d.type!=y||x(a)||e||b(c);t=l&&"keydown"==d.type};h._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)q(a[d],b,c)};w(a,"keypress",d);w(a,"keydown",d);w(a,"keyup",d)}if(r){var p={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",
                                                                                                                                                                                                                                                                                                                                                                                                                                                                     18:"alt",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},t={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},D={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},C={option:"alt",command:"meta","return":"enter",
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},n;for(f=1;20>f;++f)p[111+f]="f"+f;for(f=0;9>=f;++f)p[f+96]=f.toString();c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};
    this._directMap={};return this};c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||E(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(p[b]=a[b]);n=null};c.init=function(){var a=c(v),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,
    arguments)}}(b))};c.init();r.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=c);"function"===typeof define&&define.amd&&define(function(){return c})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null);;
(function (a) { var c = {}, d = a.prototype.stopCallback; a.prototype.stopCallback = function (e, b, a, f) { return this.paused ? !0 : c[a] || c[f] ? !1 : d.call(this, e, b, a) }; a.prototype.bindGlobal = function (a, b, d) { this.bind(a, b, d); if (a instanceof Array) for (b = 0; b < a.length; b++)c[a[b]] = !0; else c[a] = !0 }; a.init() })(Mousetrap);;
/**
 * This script gives you the zone info key representing your device's time zone setting.
 *
 * @name jsTimezoneDetect
 * @version 1.0.5
 * @author Jon Nylander
 * @license MIT License - http://www.opensource.org/licenses/mit-license.php
 *
 * For usage and examples, visit:
 * http://pellepim.bitbucket.org/jstz/
 *
 * Copyright (c) Jon Nylander
 */

/*jslint undef: true */
/*global console, exports*/

(function(root) {
  /**
   * Namespace to hold all the code for timezone detection.
   */
  var jstz = (function () {
      'use strict';
      var HEMISPHERE_SOUTH = 's',
          
          /**
           * Gets the offset in minutes from UTC for a certain date.
           * @param {Date} date
           * @returns {Number}
           */
          get_date_offset = function (date) {
              var offset = -date.getTimezoneOffset();
              return (offset !== null ? offset : 0);
          },

          get_date = function (year, month, date) {
              var d = new Date();
              if (year !== undefined) {
                d.setFullYear(year);
              }
              d.setMonth(month);
              d.setDate(date);
              return d;
          },

          get_january_offset = function (year) {
              return get_date_offset(get_date(year, 0 ,2));
          },

          get_june_offset = function (year) {
              return get_date_offset(get_date(year, 5, 2));
          },

          /**
           * Private method.
           * Checks whether a given date is in daylight saving time.
           * If the date supplied is after august, we assume that we're checking
           * for southern hemisphere DST.
           * @param {Date} date
           * @returns {Boolean}
           */
          date_is_dst = function (date) {
              var is_southern = date.getMonth() > 7,
                  base_offset = is_southern ? get_june_offset(date.getFullYear()) : 
                                              get_january_offset(date.getFullYear()),
                  date_offset = get_date_offset(date),
                  is_west = base_offset < 0,
                  dst_offset = base_offset - date_offset;
                  
              if (!is_west && !is_southern) {
                  return dst_offset < 0;
              }

              return dst_offset !== 0;
          },

          /**
           * This function does some basic calculations to create information about
           * the user's timezone. It uses REFERENCE_YEAR as a solid year for which
           * the script has been tested rather than depend on the year set by the
           * client device.
           *
           * Returns a key that can be used to do lookups in jstz.olson.timezones.
           * eg: "720,1,2". 
           *
           * @returns {String}
           */

          lookup_key = function () {
              var january_offset = get_january_offset(),
                  june_offset = get_june_offset(),
                  diff = january_offset - june_offset;

              if (diff < 0) {
                  return january_offset + ",1";
              } else if (diff > 0) {
                  return june_offset + ",1," + HEMISPHERE_SOUTH;
              }

              return january_offset + ",0";
          },

          /**
           * Uses get_timezone_info() to formulate a key to use in the olson.timezones dictionary.
           *
           * Returns a primitive object on the format:
           * {'timezone': TimeZone, 'key' : 'the key used to find the TimeZone object'}
           *
           * @returns Object
           */
          determine = function () {
              var key = lookup_key();
              return new jstz.TimeZone(jstz.olson.timezones[key]);
          },

          /**
           * This object contains information on when daylight savings starts for
           * different timezones.
           *
           * The list is short for a reason. Often we do not have to be very specific
           * to single out the correct timezone. But when we do, this list comes in
           * handy.
           *
           * Each value is a date denoting when daylight savings starts for that timezone.
           */
          dst_start_for = function (tz_name) {

            var ru_pre_dst_change = new Date(2010, 6, 15, 1, 0, 0, 0), // In 2010 Russia had DST, this allows us to detect Russia :)
                dst_starts = {
                    'America/Denver': new Date(2011, 2, 13, 3, 0, 0, 0),
                    'America/Mazatlan': new Date(2011, 3, 3, 3, 0, 0, 0),
                    'America/Chicago': new Date(2011, 2, 13, 3, 0, 0, 0),
                    'America/Mexico_City': new Date(2011, 3, 3, 3, 0, 0, 0),
                    'America/Asuncion': new Date(2012, 9, 7, 3, 0, 0, 0),
                    'America/Santiago': new Date(2012, 9, 3, 3, 0, 0, 0),
                    'America/Campo_Grande': new Date(2012, 9, 21, 5, 0, 0, 0),
                    'America/Montevideo': new Date(2011, 9, 2, 3, 0, 0, 0),
                    'America/Sao_Paulo': new Date(2011, 9, 16, 5, 0, 0, 0),
                    'America/Los_Angeles': new Date(2011, 2, 13, 8, 0, 0, 0),
                    'America/Santa_Isabel': new Date(2011, 3, 5, 8, 0, 0, 0),
                    'America/Havana': new Date(2012, 2, 10, 2, 0, 0, 0),
                    'America/New_York': new Date(2012, 2, 10, 7, 0, 0, 0),
                    'Europe/Helsinki': new Date(2013, 2, 31, 5, 0, 0, 0),
                    'Pacific/Auckland': new Date(2011, 8, 26, 7, 0, 0, 0),
                    'America/Halifax': new Date(2011, 2, 13, 6, 0, 0, 0),
                    'America/Goose_Bay': new Date(2011, 2, 13, 2, 1, 0, 0),
                    'America/Miquelon': new Date(2011, 2, 13, 5, 0, 0, 0),
                    'America/Godthab': new Date(2011, 2, 27, 1, 0, 0, 0),
                    'Europe/Moscow': ru_pre_dst_change,
                    'Asia/Amman': new Date(2013, 2, 29, 1, 0, 0, 0),
                    'Asia/Beirut': new Date(2013, 2, 31, 2, 0, 0, 0),
                    'Asia/Damascus': new Date(2013, 3, 6, 2, 0, 0, 0),
                    'Asia/Jerusalem': new Date(2013, 2, 29, 5, 0, 0, 0),
                    'Asia/Yekaterinburg': ru_pre_dst_change,
                    'Asia/Omsk': ru_pre_dst_change,
                    'Asia/Krasnoyarsk': ru_pre_dst_change,
                    'Asia/Irkutsk': ru_pre_dst_change,
                    'Asia/Yakutsk': ru_pre_dst_change,
                    'Asia/Vladivostok': ru_pre_dst_change,
                    'Asia/Baku': new Date(2013, 2, 31, 4, 0, 0),
                    'Asia/Yerevan': new Date(2013, 2, 31, 3, 0, 0),
                    'Asia/Kamchatka': ru_pre_dst_change,
                    'Asia/Gaza': new Date(2010, 2, 27, 4, 0, 0),
                    'Africa/Cairo': new Date(2010, 4, 1, 3, 0, 0),
                    'Europe/Minsk': ru_pre_dst_change,
                    'Pacific/Apia': new Date(2010, 10, 1, 1, 0, 0, 0),
                    'Pacific/Fiji': new Date(2010, 11, 1, 0, 0, 0),
                    'Australia/Perth': new Date(2008, 10, 1, 1, 0, 0, 0)
                };

              return dst_starts[tz_name];
          };

      return {
          determine: determine,
          date_is_dst: date_is_dst,
          dst_start_for: dst_start_for 
      };
  }());

  /**
   * Simple object to perform ambiguity check and to return name of time zone.
   */
  jstz.TimeZone = function (tz_name) {
      'use strict';
        /**
         * The keys in this object are timezones that we know may be ambiguous after
         * a preliminary scan through the olson_tz object.
         *
         * The array of timezones to compare must be in the order that daylight savings
         * starts for the regions.
         */
      var AMBIGUITIES = {
              'America/Denver':       ['America/Denver', 'America/Mazatlan'],
              'America/Chicago':      ['America/Chicago', 'America/Mexico_City'],
              'America/Santiago':     ['America/Santiago', 'America/Asuncion', 'America/Campo_Grande'],
              'America/Montevideo':   ['America/Montevideo', 'America/Sao_Paulo'],
              'Asia/Beirut':          ['Asia/Amman', 'Asia/Jerusalem', 'Asia/Beirut', 'Europe/Helsinki','Asia/Damascus'],
              'Pacific/Auckland':     ['Pacific/Auckland', 'Pacific/Fiji'],
              'America/Los_Angeles':  ['America/Los_Angeles', 'America/Santa_Isabel'],
              'America/New_York':     ['America/Havana', 'America/New_York'],
              'America/Halifax':      ['America/Goose_Bay', 'America/Halifax'],
              'America/Godthab':      ['America/Miquelon', 'America/Godthab'],
              'Asia/Dubai':           ['Europe/Moscow'],
              'Asia/Dhaka':           ['Asia/Yekaterinburg'],
              'Asia/Jakarta':         ['Asia/Omsk'],
              'Asia/Shanghai':        ['Asia/Krasnoyarsk', 'Australia/Perth'],
              'Asia/Tokyo':           ['Asia/Irkutsk'],
              'Australia/Brisbane':   ['Asia/Yakutsk'],
              'Pacific/Noumea':       ['Asia/Vladivostok'],
              'Pacific/Tarawa':       ['Asia/Kamchatka', 'Pacific/Fiji'],
              'Pacific/Tongatapu':    ['Pacific/Apia'],
              'Asia/Baghdad':         ['Europe/Minsk'],
              'Asia/Baku':            ['Asia/Yerevan','Asia/Baku'],
              'Africa/Johannesburg':  ['Asia/Gaza', 'Africa/Cairo']
          },

          timezone_name = tz_name,
          
          /**
           * Checks if a timezone has possible ambiguities. I.e timezones that are similar.
           *
           * For example, if the preliminary scan determines that we're in America/Denver.
           * We double check here that we're really there and not in America/Mazatlan.
           *
           * This is done by checking known dates for when daylight savings start for different
           * timezones during 2010 and 2011.
           */
          ambiguity_check = function () {
              var ambiguity_list = AMBIGUITIES[timezone_name],
                  length = ambiguity_list.length,
                  i = 0,
                  tz = ambiguity_list[0];

              for (; i < length; i += 1) {
                  tz = ambiguity_list[i];

                  if (jstz.date_is_dst(jstz.dst_start_for(tz))) {
                      timezone_name = tz;
                      return;
                  }
              }
          },

          /**
           * Checks if it is possible that the timezone is ambiguous.
           */
          is_ambiguous = function () {
              return typeof (AMBIGUITIES[timezone_name]) !== 'undefined';
          };

      if (is_ambiguous()) {
          ambiguity_check();
      }

      return {
          name: function () {
              return timezone_name;
          }
      };
  };

  jstz.olson = {};

  /*
   * The keys in this dictionary are comma separated as such:
   *
   * First the offset compared to UTC time in minutes.
   *
   * Then a flag which is 0 if the timezone does not take daylight savings into account and 1 if it
   * does.
   *
   * Thirdly an optional 's' signifies that the timezone is in the southern hemisphere,
   * only interesting for timezones with DST.
   *
   * The mapped arrays is used for constructing the jstz.TimeZone object from within
   * jstz.determine_timezone();
   */
  jstz.olson.timezones = {
      '-720,0'   : 'Pacific/Majuro',
      '-660,0'   : 'Pacific/Pago_Pago',
      '-600,1'   : 'America/Adak',
      '-600,0'   : 'Pacific/Honolulu',
      '-570,0'   : 'Pacific/Marquesas',
      '-540,0'   : 'Pacific/Gambier',
      '-540,1'   : 'America/Anchorage',
      '-480,1'   : 'America/Los_Angeles',
      '-480,0'   : 'Pacific/Pitcairn',
      '-420,0'   : 'America/Phoenix',
      '-420,1'   : 'America/Denver',
      '-360,0'   : 'America/Guatemala',
      '-360,1'   : 'America/Chicago',
      '-360,1,s' : 'Pacific/Easter',
      '-300,0'   : 'America/Bogota',
      '-300,1'   : 'America/New_York',
      '-270,0'   : 'America/Caracas',
      '-240,1'   : 'America/Halifax',
      '-240,0'   : 'America/Santo_Domingo',
      '-240,1,s' : 'America/Santiago',
      '-210,1'   : 'America/St_Johns',
      '-180,1'   : 'America/Godthab',
      '-180,0'   : 'America/Argentina/Buenos_Aires',
      '-180,1,s' : 'America/Montevideo',
      '-120,0'   : 'America/Noronha',
      '-120,1'   : 'America/Noronha',
      '-60,1'    : 'Atlantic/Azores',
      '-60,0'    : 'Atlantic/Cape_Verde',
      '0,0'      : 'UTC',
      '0,1'      : 'Europe/London',
      '60,1'     : 'Europe/Berlin',
      '60,0'     : 'Africa/Lagos',
      '60,1,s'   : 'Africa/Windhoek',
      '120,1'    : 'Asia/Beirut',
      '120,0'    : 'Africa/Johannesburg',
      '180,0'    : 'Asia/Baghdad',
      '180,1'    : 'Europe/Moscow',
      '210,1'    : 'Asia/Tehran',
      '240,0'    : 'Asia/Dubai',
      '240,1'    : 'Asia/Baku',
      '270,0'    : 'Asia/Kabul',
      '300,1'    : 'Asia/Yekaterinburg',
      '300,0'    : 'Asia/Karachi',
      '330,0'    : 'Asia/Kolkata',
      '345,0'    : 'Asia/Kathmandu',
      '360,0'    : 'Asia/Dhaka',
      '360,1'    : 'Asia/Omsk',
      '390,0'    : 'Asia/Rangoon',
      '420,1'    : 'Asia/Krasnoyarsk',
      '420,0'    : 'Asia/Jakarta',
      '480,0'    : 'Asia/Shanghai',
      '480,1'    : 'Asia/Irkutsk',
      '525,0'    : 'Australia/Eucla',
      '525,1,s'  : 'Australia/Eucla',
      '540,1'    : 'Asia/Yakutsk',
      '540,0'    : 'Asia/Tokyo',
      '570,0'    : 'Australia/Darwin',
      '570,1,s'  : 'Australia/Adelaide',
      '600,0'    : 'Australia/Brisbane',
      '600,1'    : 'Asia/Vladivostok',
      '600,1,s'  : 'Australia/Sydney',
      '630,1,s'  : 'Australia/Lord_Howe',
      '660,1'    : 'Asia/Kamchatka',
      '660,0'    : 'Pacific/Noumea',
      '690,0'    : 'Pacific/Norfolk',
      '720,1,s'  : 'Pacific/Auckland',
      '720,0'    : 'Pacific/Tarawa',
      '765,1,s'  : 'Pacific/Chatham',
      '780,0'    : 'Pacific/Tongatapu',
      '780,1,s'  : 'Pacific/Apia',
      '840,0'    : 'Pacific/Kiritimati'
  };

  if (typeof exports !== 'undefined') {
    exports.jstz = jstz;
  } else {
    root.jstz = jstz;
  }
})(this);
;
//     Underscore.js 1.8.2
//     http://underscorejs.org
//     (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.
(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=d(e,i,4);var o=!w(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=b(r,e);for(var u=null!=t&&t.length,i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t){var r=S.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||o,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=S[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var e=this,u=e._,i=Array.prototype,o=Object.prototype,a=Function.prototype,c=i.push,l=i.slice,f=o.toString,s=o.hasOwnProperty,p=Array.isArray,h=Object.keys,v=a.bind,g=Object.create,y=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):e._=m,m.VERSION="1.8.2";var d=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},b=function(n,t,r){return null==n?m.identity:m.isFunction(n)?d(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return b(n,t,1/0)};var x=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var l=o[c];t&&r[l]!==void 0||(r[l]=i[l])}return r}},_=function(n){if(!m.isObject(n))return{};if(g)return g(n);y.prototype=n;var t=new y;return y.prototype=null,t},j=Math.pow(2,53)-1,w=function(n){var t=n&&n.length;return"number"==typeof t&&t>=0&&j>=t};m.each=m.forEach=function(n,t,r){t=d(t,r);var e,u;if(w(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=b(t,r);for(var e=!w(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=w(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=b(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(b(t)),r)},m.every=m.all=function(n,t,r){t=b(t,r);for(var e=!w(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=b(t,r);for(var e=!w(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r){return w(n)||(n=m.values(n)),m.indexOf(n,t,"number"==typeof r&&r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=w(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=b(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=w(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=b(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=w(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(w(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=b(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var A=function(n){return function(t,r,e){var u={};return r=b(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=A(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=A(function(n,t,r){n[r]=t}),m.countBy=A(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):w(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:w(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=b(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var k=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=n&&n.length;a>o;o++){var c=n[o];if(w(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=k(c,t,r));var l=0,f=c.length;for(u.length+=f;f>l;)u[i++]=c[l++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return k(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){if(null==n)return[];m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=b(r,e));for(var u=[],i=[],o=0,a=n.length;a>o;o++){var c=n[o],l=r?r(c,o,n):c;t?(o&&i===l||u.push(c),i=l):r?m.contains(i,l)||(i.push(l),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(k(arguments,!0,!0))},m.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=k(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,"length").length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=n&&n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.indexOf=function(n,t,r){var e=0,u=n&&n.length;if("number"==typeof r)e=0>r?Math.max(0,u+r):r;else if(r&&u)return e=m.sortedIndex(n,t),n[e]===t?e:-1;if(t!==t)return m.findIndex(l.call(n,e),m.isNaN);for(;u>e;e++)if(n[e]===t)return e;return-1},m.lastIndexOf=function(n,t,r){var e=n?n.length:0;if("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1)),t!==t)return m.findLastIndex(l.call(n,0,e),m.isNaN);for(;--e>=0;)if(n[e]===t)return e;return-1},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=b(r,e,1);for(var u=r(t),i=0,o=n.length;o>i;){var a=Math.floor((i+o)/2);r(n[a])<u?i=a+1:o=a}return i},m.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var O=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=_(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(v&&n.bind===v)return v.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return O(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e<arguments.length;)i.push(arguments[e++]);return O(n,r,this,this,i)};return r},m.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var l=m.now();a||r.leading!==!1||(a=l);var f=t-(l-a);return e=this,u=arguments,0>=f||f>t?(o&&(clearTimeout(o),o=null),a=l,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,f)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var l=m.now()-o;t>l&&l>=0?e=setTimeout(c,t-l):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var F=!{toString:null}.propertyIsEnumerable("toString"),S=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(h)return h(n);var t=[];for(var e in n)m.has(n,e)&&t.push(e);return F&&r(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var e in n)t.push(e);return F&&r(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=b(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=x(m.allKeys),m.extendOwn=m.assign=x(m.keys),m.findKey=function(n,t,r){t=b(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=d(t,r)):(u=k(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var l=u[a],f=o[l];e(f,l,o)&&(i[l]=f)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(k(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=x(m.allKeys,!0),m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var E=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=f.call(n);if(u!==f.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!E(n[c],t[c],r,e))return!1}else{var l,s=m.keys(n);if(c=s.length,m.keys(t).length!==c)return!1;for(;c--;)if(l=s[c],!m.has(t,l)||!E(n[l],t[l],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return E(n,t)},m.isEmpty=function(n){return null==n?!0:w(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=p||function(n){return"[object Array]"===f.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return f.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===f.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&s.call(n,t)},m.noConflict=function(){return e._=u,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=function(n){return function(t){return null==t?void 0:t[n]}},m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=d(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var M={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},N=m.invert(M),I=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=I(M),m.unescape=I(N),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var B=0;m.uniqueId=function(n){var t=++B+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,R={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},q=/\\|'|\r|\n|\u2028|\u2029/g,K=function(n){return"\\"+R[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||T).source,(t.interpolate||T).source,(t.evaluate||T).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(q,K),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},l=t.variable||"obj";return c.source="function("+l+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var z=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return c.apply(n,arguments),z(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=i[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],z(this,r)}}),m.each(["concat","join","slice"],function(n){var t=i[n];m.prototype[n]=function(){return z(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this);
//# sourceMappingURL=underscore-min.map;

(function (window, undefined) {
    var workerIFrames = [], nextOpId = 0, pendingOperations = {}, initializePromise = null, useLocal = true;

    function isQuotaExceededError(e) {
        return e && e.code && e.code === DOMException.QUOTA_EXCEEDED_ERR;
    }

    function handleWorkerMessage(event) {
        var iframe, message, state;

        if (workerIFrames.filter(function (f) {
            return f.id.toLowerCase() === event.origin.toLowerCase();
        }).length === 0)
            return;

        if (!event.data || event.data.opId === undefined)
            return;

        message = event.data;
        state = pendingOperations[message.opId.toString()];
        state.pendingWorkers--;

        if (!state.deferred.promise.isPending)
            return;

        if (message.error && message.type !== 'setItem') {
            state.deferred.reject(message.error);
            return;
        }

        switch (message.type) {
            case 'ready':
                if (state.pendingWorkers === 0)
                    state.deferred.resolve(null);
                break;

            case 'getItem':
                if (message.data) {
                    state.deferred.resolve(message.data);
                } else if (state.pendingWorkers === 0) {
                    state.deferred.reject(null);
                }
                break;

            case 'setItem':
                if (message.error && isQuotaExceededError(message.error) && state.pendingWorkers > 0) {
                    delete message.error;
                    iframe = workerIFrames[workerIFrames.length - state.pendingWorkers];
                    iframe.contentWindow.postMessage(message, iframe.id);
                } else if (message.error) {
                    state.deferred.reject(message.error);
                } else {
                    state.deferred.resolve(null);
                }
                break;

            case 'removeItem':
            case 'clear':
                if (state.pendingWorkers === 0)
                    state.deferred.resolve(null);
                break;

            default:
                throw new Error('unexpected message type: ' + message.type);
        }
    }

    function createStorageOperation() {
        var state = { pendingWorkers: workerIFrames.length, deferred: Q.defer(), opId: nextOpId };
        pendingOperations[state.opId] = state;
        state.deferred.promise.finally(function () {
            return delete pendingOperations[state.opId];
        });
        nextOpId++;
        return state;
    }

    function broadcastMessage(type, key) {
        var state = createStorageOperation(), message = { opId: state.opId, type: type }, i;

        if (key)
            message.key = key;

        for (i = 0; i < workerIFrames.length; i++) {
            workerIFrames[i].contentWindow.postMessage(message, workerIFrames[i].id);
        }

        return state.deferred.promise;
    }

    function assertReady() {
        if (initializePromise === null)
            throw new Error('initialize has not been called.');
        if (!initializePromise.isFulfilled)
            throw new Error('initialize has not completed.');
    }

    function getOrigin(url) {
        var pathArray = url.split('/'), protocol = pathArray[0], host = pathArray[2], origin = protocol + '//' + host;
        return origin;
    }

    function initialize(urls, canUseLocal) {
        var targetOrigin = window.location.protocol + "//" + window.location.host, state, iframe, origin, i;

        if (initializePromise !== null)
            throw new Error('initialize has already been called.');

        if (typeof urls === 'undefined' || urls === null)
            urls = ['http://worker.storig1.in', 'http://worker.storig2.in', 'http://worker.storig3.in'];

        if (typeof canUseLocal === 'boolean')
            useLocal = canUseLocal;

        for (i = 0; i < urls.length; i++) {
            origin = getOrigin(urls[i]);

            if (origin.toLowerCase() === targetOrigin.toLowerCase())
                throw new Error("The urls argument cannot contain an item whose origin matches the primary origin.  Primary origin: '" + origin + "'.");
            if (workerIFrames.filter(function (f) {
                return f.id.toLowerCase() === origin.toLowerCase();
            }).length > 0)
                throw new Error("The urls argument cannot contain multiple items with the same origin.  Duplicate origin: '" + origin + "'.");

            iframe = document.createElement('iframe');
            workerIFrames.push(iframe);
            iframe.id = origin;
            iframe.style.display = "none";
            iframe.src = urls[i];
        }

        state = createStorageOperation();
        initializePromise = state.deferred.promise;

        if (window.addEventListener) {
            window.addEventListener("message", handleWorkerMessage, false);
        } else if (window.attachEvent) {
            window.attachEvent("onmessage", handleWorkerMessage);
        } else {
            window.onmessage = handleWorkerMessage;
        }

        workerIFrames.forEach(function (w) {
            return document.body.appendChild(w);
        });

        return initializePromise;
    }

    function getItem(key) {
        var data = null;

        assertReady();

        if (useLocal)
            data = localStorage.getItem(key);
        if (data)
            return Q.fcall(function () {
                return data;
            });

        return broadcastMessage('getItem', key).then(function (value) {
            if (value === null)
                throw 'key not found';
            return value;
        });
    }

    function setItem(key, data) {
        var state, message, iframe;

        assertReady();

        if (useLocal) {
            try  {
                localStorage.setItem(key, data);
                return Q.fcall(function () {
                    return null;
                });
            } catch (e) {
                if (!isQuotaExceededError(e)) {
                    return Q.fcall(function () {
                        throw e;
                        return null;
                    });
                }
            }
        }

        state = createStorageOperation();
        message = { opId: state.opId, type: 'setItem', key: key, data: data };
        workerIFrames[0].contentWindow.postMessage(message, workerIFrames[0].id);

        return state.deferred.promise;
    }

    function removeThenSetItem(key, data) {
        return removeItem(key).then(function () {
            return setItem(key, data);
        });
    }

    function removeItem(key) {
        assertReady();

        if (useLocal) {
            localStorage.removeItem(key);
        }
        return broadcastMessage('removeItem', key);
    }

    function clear() {
        assertReady();

        if (useLocal) {
            localStorage.clear();
        }
        return broadcastMessage('clear');
    }

    var exports = {
        initialize: initialize,
        getItem: getItem,
        setItem: removeThenSetItem,
        removeItem: removeItem,
        clear: clear,
        isQuotaExceededError: isQuotaExceededError
    };

    window.storig = exports;
})(window);
;
