/* Minification failed. Returning unminified contents.
(3051,35-36): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: b
(3051,28-29): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: g
(3051,21-22): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: r
(3052,35-36): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: b
(3052,28-29): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: g
(3052,21-22): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: r
(3053,36-37): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: b
(3053,29-30): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: g
(3053,21-22): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: r
(3054,36-37): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: b
(3054,29-30): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: g
(3054,21-22): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: r
(3055,36-37): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: b
(3055,28-29): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: g
(3055,21-22): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: r
(3056,36-37): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: b
(3056,28-29): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: g
(3056,21-22): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: r
 */
/* qTip2 v2.2.1 | Plugins: tips | Styles: core basic css3 | qtip2.com | Licensed MIT | Sun Sep 07 2014 08:23:14 */

/*!
 * EventEmitter v4.2.6 - git.io/ee
 * Oliver Caldwell
 * MIT license
 * @preserve
 */
(function(){"use strict";function a(){}function b(a,b){for(var c=a.length;c--;)if(a[c].listener===b)return c;return-1}function c(a){return function(){return this[a].apply(this,arguments)}}var d=a.prototype,e=this,f=e.EventEmitter;d.getListeners=function(a){var b,c,d=this._getEvents();if("object"==typeof a){b={};for(c in d)d.hasOwnProperty(c)&&a.test(c)&&(b[c]=d[c])}else b=d[a]||(d[a]=[]);return b},d.flattenListeners=function(a){var b,c=[];for(b=0;b<a.length;b+=1)c.push(a[b].listener);return c},d.getListenersAsObject=function(a){var b,c=this.getListeners(a);return c instanceof Array&&(b={},b[a]=c),b||c},d.addListener=function(a,c){var d,e=this.getListenersAsObject(a),f="object"==typeof c;for(d in e)e.hasOwnProperty(d)&&-1===b(e[d],c)&&e[d].push(f?c:{listener:c,once:!1});return this},d.on=c("addListener"),d.addOnceListener=function(a,b){return this.addListener(a,{listener:b,once:!0})},d.once=c("addOnceListener"),d.defineEvent=function(a){return this.getListeners(a),this},d.defineEvents=function(a){for(var b=0;b<a.length;b+=1)this.defineEvent(a[b]);return this},d.removeListener=function(a,c){var d,e,f=this.getListenersAsObject(a);for(e in f)f.hasOwnProperty(e)&&(d=b(f[e],c),-1!==d&&f[e].splice(d,1));return this},d.off=c("removeListener"),d.addListeners=function(a,b){return this.manipulateListeners(!1,a,b)},d.removeListeners=function(a,b){return this.manipulateListeners(!0,a,b)},d.manipulateListeners=function(a,b,c){var d,e,f=a?this.removeListener:this.addListener,g=a?this.removeListeners:this.addListeners;if("object"!=typeof b||b instanceof RegExp)for(d=c.length;d--;)f.call(this,b,c[d]);else for(d in b)b.hasOwnProperty(d)&&(e=b[d])&&("function"==typeof e?f.call(this,d,e):g.call(this,d,e));return this},d.removeEvent=function(a){var b,c=typeof a,d=this._getEvents();if("string"===c)delete d[a];else if("object"===c)for(b in d)d.hasOwnProperty(b)&&a.test(b)&&delete d[b];else delete this._events;return this},d.removeAllListeners=c("removeEvent"),d.emitEvent=function(a,b){var c,d,e,f,g=this.getListenersAsObject(a);for(e in g)if(g.hasOwnProperty(e))for(d=g[e].length;d--;)c=g[e][d],c.once===!0&&this.removeListener(a,c.listener),f=c.listener.apply(this,b||[]),f===this._getOnceReturnValue()&&this.removeListener(a,c.listener);return this},d.trigger=c("emitEvent"),d.emit=function(a){var b=Array.prototype.slice.call(arguments,1);return this.emitEvent(a,b)},d.setOnceReturnValue=function(a){return this._onceReturnValue=a,this},d._getOnceReturnValue=function(){return this.hasOwnProperty("_onceReturnValue")?this._onceReturnValue:!0},d._getEvents=function(){return this._events||(this._events={})},a.noConflict=function(){return e.EventEmitter=f,a},"function"==typeof define&&define.amd?define(function(){return a}):"object"==typeof module&&module.exports?module.exports=a:this.EventEmitter=a}).call(this),/*!
 * eventie v1.0.3
 * event binding helper
 *   eventie.bind( elem, 'click', myFn )
 *   eventie.unbind( elem, 'click', myFn )
 */
function(a){"use strict";var b=document.documentElement,c=function(){};b.addEventListener?c=function(a,b,c){a.addEventListener(b,c,!1)}:b.attachEvent&&(c=function(b,c,d){b[c+d]=d.handleEvent?function(){var b=a.event;b.target=b.target||b.srcElement,d.handleEvent.call(d,b)}:function(){var c=a.event;c.target=c.target||c.srcElement,d.call(b,c)},b.attachEvent("on"+c,b[c+d])});var d=function(){};b.removeEventListener?d=function(a,b,c){a.removeEventListener(b,c,!1)}:b.detachEvent&&(d=function(a,b,c){a.detachEvent("on"+b,a[b+c]);try{delete a[b+c]}catch(d){a[b+c]=void 0}});var e={bind:c,unbind:d};"function"==typeof define&&define.amd?define(e):a.eventie=e}(this),/*!
 * imagesLoaded v3.0.2
 * JavaScript is all like "You images are done yet or what?"
 */
function(a){"use strict";function b(a,b){for(var c in b)a[c]=b[c];return a}function c(a){return"[object Array]"===i.call(a)}function d(a){var b=[];if(c(a))b=a;else if("number"==typeof a.length)for(var d=0,e=a.length;e>d;d++)b.push(a[d]);else b.push(a);return b}function e(a,c){function e(a,c,g){if(!(this instanceof e))return new e(a,c);"string"==typeof a&&(a=document.querySelectorAll(a)),this.elements=d(a),this.options=b({},this.options),"function"==typeof c?g=c:b(this.options,c),g&&this.on("always",g),this.getImages(),f&&(this.jqDeferred=new f.Deferred);var h=this;setTimeout(function(){h.check()})}function i(a){this.img=a}e.prototype=new a,e.prototype.options={},e.prototype.getImages=function(){this.images=[];for(var a=0,b=this.elements.length;b>a;a++){var c=this.elements[a];"IMG"===c.nodeName&&this.addImage(c);for(var d=c.querySelectorAll("img"),e=0,f=d.length;f>e;e++){var g=d[e];this.addImage(g)}}},e.prototype.addImage=function(a){var b=new i(a);this.images.push(b)},e.prototype.check=function(){function a(a,e){return b.options.debug&&h&&g.log("confirm",a,e),b.progress(a),c++,c===d&&b.complete(),!0}var b=this,c=0,d=this.images.length;if(this.hasAnyBroken=!1,!d)return void this.complete();for(var e=0;d>e;e++){var f=this.images[e];f.on("confirm",a),f.check()}},e.prototype.progress=function(a){this.hasAnyBroken=this.hasAnyBroken||!a.isLoaded,this.emit("progress",this,a),this.jqDeferred&&this.jqDeferred.notify(this,a)},e.prototype.complete=function(){var a=this.hasAnyBroken?"fail":"done";if(this.isComplete=!0,this.emit(a,this),this.emit("always",this),this.jqDeferred){var b=this.hasAnyBroken?"reject":"resolve";this.jqDeferred[b](this)}},f&&(f.fn.imagesLoaded=function(a,b){var c=new e(this,a,b);return c.jqDeferred.promise(f(this))});var j={};return i.prototype=new a,i.prototype.check=function(){var a=j[this.img.src];if(a)return void this.useCached(a);if(j[this.img.src]=this,this.img.complete&&void 0!==this.img.naturalWidth)return void this.confirm(0!==this.img.naturalWidth,"naturalWidth");var b=this.proxyImage=new Image;c.bind(b,"load",this),c.bind(b,"error",this),b.src=this.img.src},i.prototype.useCached=function(a){if(a.isConfirmed)this.confirm(a.isLoaded,"cached was confirmed");else{var b=this;a.on("confirm",function(a){return b.confirm(a.isLoaded,"cache emitted confirmed"),!0})}},i.prototype.confirm=function(a,b){this.isConfirmed=!0,this.isLoaded=a,this.emit("confirm",this,b)},i.prototype.handleEvent=function(a){var b="on"+a.type;this[b]&&this[b](a)},i.prototype.onload=function(){this.confirm(!0,"onload"),this.unbindProxyEvents()},i.prototype.onerror=function(){this.confirm(!1,"onerror"),this.unbindProxyEvents()},i.prototype.unbindProxyEvents=function(){c.unbind(this.proxyImage,"load",this),c.unbind(this.proxyImage,"error",this)},e}var f=a.jQuery,g=a.console,h="undefined"!=typeof g,i=Object.prototype.toString;"function"==typeof define&&define.amd?define(["eventEmitter","eventie"],e):a.imagesLoaded=e(a.EventEmitter,a.eventie)}(window);
//# sourceMappingURL=imagesloaded.pkg.min.js.map;
/* qTip2 v2.2.1 | Plugins: tips | Styles: core basic css3 | qtip2.com | Licensed MIT | Sun Sep 07 2014 08:23:14 */

!function(a,b,c){!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)}(function(d){"use strict";function e(a,b,c,e){this.id=c,this.target=a,this.tooltip=D,this.elements={target:a},this._id=P+"-"+c,this.timers={img:{}},this.options=b,this.plugins={},this.cache={event:{},target:d(),disabled:C,attr:e,onTooltip:C,lastClass:""},this.rendered=this.destroyed=this.disabled=this.waiting=this.hiddenDuringWait=this.positioning=this.triggering=C}function f(a){return a===D||"object"!==d.type(a)}function g(a){return!(d.isFunction(a)||a&&a.attr||a.length||"object"===d.type(a)&&(a.jquery||a.then))}function h(a){var b,c,e,h;return f(a)?C:(f(a.metadata)&&(a.metadata={type:a.metadata}),"content"in a&&(b=a.content,f(b)||b.jquery||b.done?b=a.content={text:c=g(b)?C:b}:c=b.text,"ajax"in b&&(e=b.ajax,h=e&&e.once!==C,delete b.ajax,b.text=function(a,b){var f=c||d(this).attr(b.options.content.attr)||"Loading...",g=d.ajax(d.extend({},e,{context:b})).then(e.success,D,e.error).then(function(a){return a&&h&&b.set("content.text",a),a},function(a,c,d){b.destroyed||0===a.status||b.set("content.text",c+": "+d)});return h?f:(b.set("content.text",f),g)}),"title"in b&&(d.isPlainObject(b.title)&&(b.button=b.title.button,b.title=b.title.text),g(b.title||C)&&(b.title=C))),"position"in a&&f(a.position)&&(a.position={my:a.position,at:a.position}),"show"in a&&f(a.show)&&(a.show=a.show.jquery?{target:a.show}:a.show===B?{ready:B}:{event:a.show}),"hide"in a&&f(a.hide)&&(a.hide=a.hide.jquery?{target:a.hide}:{event:a.hide}),"style"in a&&f(a.style)&&(a.style={classes:a.style}),d.each(O,function(){this.sanitize&&this.sanitize(a)}),a)}function i(a,b){for(var c,d=0,e=a,f=b.split(".");e=e[f[d++]];)d<f.length&&(c=e);return[c||a,f.pop()]}function j(a,b){var c,d,e;for(c in this.checks)for(d in this.checks[c])(e=new RegExp(d,"i").exec(a))&&(b.push(e),("builtin"===c||this.plugins[c])&&this.checks[c][d].apply(this.plugins[c]||this,b))}function k(a){return S.concat("").join(a?"-"+a+" ":" ")}function l(a,b){return b>0?setTimeout(d.proxy(a,this),b):void a.call(this)}function m(a){this.tooltip.hasClass(Z)||(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this.timers.show=l.call(this,function(){this.toggle(B,a)},this.options.show.delay))}function n(a){if(!this.tooltip.hasClass(Z)&&!this.destroyed){var b=d(a.relatedTarget),c=b.closest(T)[0]===this.tooltip[0],e=b[0]===this.options.show.target[0];if(clearTimeout(this.timers.show),clearTimeout(this.timers.hide),this!==b[0]&&"mouse"===this.options.position.target&&c||this.options.hide.fixed&&/mouse(out|leave|move)/.test(a.type)&&(c||e))try{a.preventDefault(),a.stopImmediatePropagation()}catch(f){}else this.timers.hide=l.call(this,function(){this.toggle(C,a)},this.options.hide.delay,this)}}function o(a){!this.tooltip.hasClass(Z)&&this.options.hide.inactive&&(clearTimeout(this.timers.inactive),this.timers.inactive=l.call(this,function(){this.hide(a)},this.options.hide.inactive))}function p(a){this.rendered&&this.tooltip[0].offsetWidth>0&&this.reposition(a)}function q(a,c,e){d(b.body).delegate(a,(c.split?c:c.join("."+P+" "))+"."+P,function(){var a=w.api[d.attr(this,R)];a&&!a.disabled&&e.apply(a,arguments)})}function r(a,c,f){var g,i,j,k,l,m=d(b.body),n=a[0]===b?m:a,o=a.metadata?a.metadata(f.metadata):D,p="html5"===f.metadata.type&&o?o[f.metadata.name]:D,q=a.data(f.metadata.name||"qtipopts");try{q="string"==typeof q?d.parseJSON(q):q}catch(r){}if(k=d.extend(B,{},w.defaults,f,"object"==typeof q?h(q):D,h(p||o)),i=k.position,k.id=c,"boolean"==typeof k.content.text){if(j=a.attr(k.content.attr),k.content.attr===C||!j)return C;k.content.text=j}if(i.container.length||(i.container=m),i.target===C&&(i.target=n),k.show.target===C&&(k.show.target=n),k.show.solo===B&&(k.show.solo=i.container.closest("body")),k.hide.target===C&&(k.hide.target=n),k.position.viewport===B&&(k.position.viewport=i.container),i.container=i.container.eq(0),i.at=new y(i.at,B),i.my=new y(i.my),a.data(P))if(k.overwrite)a.qtip("destroy",!0);else if(k.overwrite===C)return C;return a.attr(Q,c),k.suppress&&(l=a.attr("title"))&&a.removeAttr("title").attr(_,l).attr("title",""),g=new e(a,k,c,!!j),a.data(P,g),g}function s(a){return a.charAt(0).toUpperCase()+a.slice(1)}function t(a,b){var d,e,f=b.charAt(0).toUpperCase()+b.slice(1),g=(b+" "+ob.join(f+" ")+f).split(" "),h=0;if(nb[b])return a.css(nb[b]);for(;d=g[h++];)if((e=a.css(d))!==c)return nb[b]=d,e}function u(a,b){return Math.ceil(parseFloat(t(a,b)))}function v(a,b){this._ns="tip",this.options=b,this.offset=b.offset,this.size=[b.width,b.height],this.init(this.qtip=a)}var w,x,y,z,A,B=!0,C=!1,D=null,E="x",F="y",G="width",H="height",I="top",J="left",K="bottom",L="right",M="center",N="shift",O={},P="qtip",Q="data-hasqtip",R="data-qtip-id",S=["ui-widget","ui-tooltip"],T="."+P,U="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),V=P+"-fixed",W=P+"-default",X=P+"-focus",Y=P+"-hover",Z=P+"-disabled",$="_replacedByqTip",_="oldtitle",ab={ie:function(){for(var a=4,c=b.createElement("div");(c.innerHTML="<!--[if gt IE "+a+"]><i></i><![endif]-->")&&c.getElementsByTagName("i")[0];a+=1);return a>4?a:0/0}(),iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||C};x=e.prototype,x._when=function(a){return d.when.apply(d,a)},x.render=function(a){if(this.rendered||this.destroyed)return this;var b,c=this,e=this.options,f=this.cache,g=this.elements,h=e.content.text,i=e.content.title,j=e.content.button,k=e.position,l=("."+this._id+" ",[]);return d.attr(this.target[0],"aria-describedby",this._id),f.posClass=this._createPosClass((this.position={my:k.my,at:k.at}).my),this.tooltip=g.tooltip=b=d("<div/>",{id:this._id,"class":[P,W,e.style.classes,f.posClass].join(" "),width:e.style.width||"",height:e.style.height||"",tracking:"mouse"===k.target&&k.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":C,"aria-describedby":this._id+"-content","aria-hidden":B}).toggleClass(Z,this.disabled).attr(R,this.id).data(P,this).appendTo(k.container).append(g.content=d("<div />",{"class":P+"-content",id:this._id+"-content","aria-atomic":B})),this.rendered=-1,this.positioning=B,i&&(this._createTitle(),d.isFunction(i)||l.push(this._updateTitle(i,C))),j&&this._createButton(),d.isFunction(h)||l.push(this._updateContent(h,C)),this.rendered=B,this._setWidget(),d.each(O,function(a){var b;"render"===this.initialize&&(b=this(c))&&(c.plugins[a]=b)}),this._unassignEvents(),this._assignEvents(),this._when(l).then(function(){c._trigger("render"),c.positioning=C,c.hiddenDuringWait||!e.show.ready&&!a||c.toggle(B,f.event,C),c.hiddenDuringWait=C}),w.api[this.id]=this,this},x.destroy=function(a){function b(){if(!this.destroyed){this.destroyed=B;var a,b=this.target,c=b.attr(_);this.rendered&&this.tooltip.stop(1,0).find("*").remove().end().remove(),d.each(this.plugins,function(){this.destroy&&this.destroy()});for(a in this.timers)clearTimeout(this.timers[a]);b.removeData(P).removeAttr(R).removeAttr(Q).removeAttr("aria-describedby"),this.options.suppress&&c&&b.attr("title",c).removeAttr(_),this._unassignEvents(),this.options=this.elements=this.cache=this.timers=this.plugins=this.mouse=D,delete w.api[this.id]}}return this.destroyed?this.target:(a===B&&"hide"!==this.triggering||!this.rendered?b.call(this):(this.tooltip.one("tooltiphidden",d.proxy(b,this)),!this.triggering&&this.hide()),this.target)},z=x.checks={builtin:{"^id$":function(a,b,c,e){var f=c===B?w.nextid:c,g=P+"-"+f;f!==C&&f.length>0&&!d("#"+g).length?(this._id=g,this.rendered&&(this.tooltip[0].id=this._id,this.elements.content[0].id=this._id+"-content",this.elements.title[0].id=this._id+"-title")):a[b]=e},"^prerender":function(a,b,c){c&&!this.rendered&&this.render(this.options.show.ready)},"^content.text$":function(a,b,c){this._updateContent(c)},"^content.attr$":function(a,b,c,d){this.options.content.text===this.target.attr(d)&&this._updateContent(this.target.attr(c))},"^content.title$":function(a,b,c){return c?(c&&!this.elements.title&&this._createTitle(),void this._updateTitle(c)):this._removeTitle()},"^content.button$":function(a,b,c){this._updateButton(c)},"^content.title.(text|button)$":function(a,b,c){this.set("content."+b,c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(this.position[b]=a[b]=new y(c,"at"===b))},"^position.container$":function(a,b,c){this.rendered&&this.tooltip.appendTo(c)},"^show.ready$":function(a,b,c){c&&(!this.rendered&&this.render(B)||this.toggle(B))},"^style.classes$":function(a,b,c,d){this.rendered&&this.tooltip.removeClass(d).addClass(c)},"^style.(width|height)":function(a,b,c){this.rendered&&this.tooltip.css(b,c)},"^style.widget|content.title":function(){this.rendered&&this._setWidget()},"^style.def":function(a,b,c){this.rendered&&this.tooltip.toggleClass(W,!!c)},"^events.(render|show|move|hide|focus|blur)$":function(a,b,c){this.rendered&&this.tooltip[(d.isFunction(c)?"":"un")+"bind"]("tooltip"+b,c)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){if(this.rendered){var a=this.options.position;this.tooltip.attr("tracking","mouse"===a.target&&a.adjust.mouse),this._unassignEvents(),this._assignEvents()}}}},x.get=function(a){if(this.destroyed)return this;var b=i(this.options,a.toLowerCase()),c=b[0][b[1]];return c.precedance?c.string():c};var bb=/^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,cb=/^prerender|show\.ready/i;x.set=function(a,b){if(this.destroyed)return this;{var c,e=this.rendered,f=C,g=this.options;this.checks}return"string"==typeof a?(c=a,a={},a[c]=b):a=d.extend({},a),d.each(a,function(b,c){if(e&&cb.test(b))return void delete a[b];var h,j=i(g,b.toLowerCase());h=j[0][j[1]],j[0][j[1]]=c&&c.nodeType?d(c):c,f=bb.test(b)||f,a[b]=[j[0],j[1],c,h]}),h(g),this.positioning=B,d.each(a,d.proxy(j,this)),this.positioning=C,this.rendered&&this.tooltip[0].offsetWidth>0&&f&&this.reposition("mouse"===g.position.target?D:this.cache.event),this},x._update=function(a,b){var c=this,e=this.cache;return this.rendered&&a?(d.isFunction(a)&&(a=a.call(this.elements.target,e.event,this)||""),d.isFunction(a.then)?(e.waiting=B,a.then(function(a){return e.waiting=C,c._update(a,b)},D,function(a){return c._update(a,b)})):a===C||!a&&""!==a?C:(a.jquery&&a.length>0?b.empty().append(a.css({display:"block",visibility:"visible"})):b.html(a),this._waitForContent(b).then(function(a){c.rendered&&c.tooltip[0].offsetWidth>0&&c.reposition(e.event,!a.length)}))):C},x._waitForContent=function(a){var b=this.cache;return b.waiting=B,(d.fn.imagesLoaded?a.imagesLoaded():d.Deferred().resolve([])).done(function(){b.waiting=C}).promise()},x._updateContent=function(a,b){this._update(a,this.elements.content,b)},x._updateTitle=function(a,b){this._update(a,this.elements.title,b)===C&&this._removeTitle(C)},x._createTitle=function(){var a=this.elements,b=this._id+"-title";a.titlebar&&this._removeTitle(),a.titlebar=d("<div />",{"class":P+"-titlebar "+(this.options.style.widget?k("header"):"")}).append(a.title=d("<div />",{id:b,"class":P+"-title","aria-atomic":B})).insertBefore(a.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(a){d(this).toggleClass("ui-state-active ui-state-focus","down"===a.type.substr(-4))}).delegate(".qtip-close","mouseover mouseout",function(a){d(this).toggleClass("ui-state-hover","mouseover"===a.type)}),this.options.content.button&&this._createButton()},x._removeTitle=function(a){var b=this.elements;b.title&&(b.titlebar.remove(),b.titlebar=b.title=b.button=D,a!==C&&this.reposition())},x._createPosClass=function(a){return P+"-pos-"+(a||this.options.position.my).abbrev()},x.reposition=function(c,e){if(!this.rendered||this.positioning||this.destroyed)return this;this.positioning=B;var f,g,h,i,j=this.cache,k=this.tooltip,l=this.options.position,m=l.target,n=l.my,o=l.at,p=l.viewport,q=l.container,r=l.adjust,s=r.method.split(" "),t=k.outerWidth(C),u=k.outerHeight(C),v=0,w=0,x=k.css("position"),y={left:0,top:0},z=k[0].offsetWidth>0,A=c&&"scroll"===c.type,D=d(a),E=q[0].ownerDocument,F=this.mouse;if(d.isArray(m)&&2===m.length)o={x:J,y:I},y={left:m[0],top:m[1]};else if("mouse"===m)o={x:J,y:I},(!r.mouse||this.options.hide.distance)&&j.origin&&j.origin.pageX?c=j.origin:!c||c&&("resize"===c.type||"scroll"===c.type)?c=j.event:F&&F.pageX&&(c=F),"static"!==x&&(y=q.offset()),E.body.offsetWidth!==(a.innerWidth||E.documentElement.clientWidth)&&(g=d(b.body).offset()),y={left:c.pageX-y.left+(g&&g.left||0),top:c.pageY-y.top+(g&&g.top||0)},r.mouse&&A&&F&&(y.left-=(F.scrollX||0)-D.scrollLeft(),y.top-=(F.scrollY||0)-D.scrollTop());else{if("event"===m?c&&c.target&&"scroll"!==c.type&&"resize"!==c.type?j.target=d(c.target):c.target||(j.target=this.elements.target):"event"!==m&&(j.target=d(m.jquery?m:this.elements.target)),m=j.target,m=d(m).eq(0),0===m.length)return this;m[0]===b||m[0]===a?(v=ab.iOS?a.innerWidth:m.width(),w=ab.iOS?a.innerHeight:m.height(),m[0]===a&&(y={top:(p||m).scrollTop(),left:(p||m).scrollLeft()})):O.imagemap&&m.is("area")?f=O.imagemap(this,m,o,O.viewport?s:C):O.svg&&m&&m[0].ownerSVGElement?f=O.svg(this,m,o,O.viewport?s:C):(v=m.outerWidth(C),w=m.outerHeight(C),y=m.offset()),f&&(v=f.width,w=f.height,g=f.offset,y=f.position),y=this.reposition.offset(m,y,q),(ab.iOS>3.1&&ab.iOS<4.1||ab.iOS>=4.3&&ab.iOS<4.33||!ab.iOS&&"fixed"===x)&&(y.left-=D.scrollLeft(),y.top-=D.scrollTop()),(!f||f&&f.adjustable!==C)&&(y.left+=o.x===L?v:o.x===M?v/2:0,y.top+=o.y===K?w:o.y===M?w/2:0)}return y.left+=r.x+(n.x===L?-t:n.x===M?-t/2:0),y.top+=r.y+(n.y===K?-u:n.y===M?-u/2:0),O.viewport?(h=y.adjusted=O.viewport(this,y,l,v,w,t,u),g&&h.left&&(y.left+=g.left),g&&h.top&&(y.top+=g.top),h.my&&(this.position.my=h.my)):y.adjusted={left:0,top:0},j.posClass!==(i=this._createPosClass(this.position.my))&&k.removeClass(j.posClass).addClass(j.posClass=i),this._trigger("move",[y,p.elem||p],c)?(delete y.adjusted,e===C||!z||isNaN(y.left)||isNaN(y.top)||"mouse"===m||!d.isFunction(l.effect)?k.css(y):d.isFunction(l.effect)&&(l.effect.call(k,this,d.extend({},y)),k.queue(function(a){d(this).css({opacity:"",height:""}),ab.ie&&this.style.removeAttribute("filter"),a()})),this.positioning=C,this):this},x.reposition.offset=function(a,c,e){function f(a,b){c.left+=b*a.scrollLeft(),c.top+=b*a.scrollTop()}if(!e[0])return c;var g,h,i,j,k=d(a[0].ownerDocument),l=!!ab.ie&&"CSS1Compat"!==b.compatMode,m=e[0];do"static"!==(h=d.css(m,"position"))&&("fixed"===h?(i=m.getBoundingClientRect(),f(k,-1)):(i=d(m).position(),i.left+=parseFloat(d.css(m,"borderLeftWidth"))||0,i.top+=parseFloat(d.css(m,"borderTopWidth"))||0),c.left-=i.left+(parseFloat(d.css(m,"marginLeft"))||0),c.top-=i.top+(parseFloat(d.css(m,"marginTop"))||0),g||"hidden"===(j=d.css(m,"overflow"))||"visible"===j||(g=d(m)));while(m=m.offsetParent);return g&&(g[0]!==k[0]||l)&&f(g,1),c};var db=(y=x.reposition.Corner=function(a,b){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,M).toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase(),this.forceY=!!b;var c=a.charAt(0);this.precedance="t"===c||"b"===c?F:E}).prototype;db.invert=function(a,b){this[a]=this[a]===J?L:this[a]===L?J:b||this[a]},db.string=function(a){var b=this.x,c=this.y,d=b!==c?"center"===b||"center"!==c&&(this.precedance===F||this.forceY)?[c,b]:[b,c]:[b];return a!==!1?d.join(" "):d},db.abbrev=function(){var a=this.string(!1);return a[0].charAt(0)+(a[1]&&a[1].charAt(0)||"")},db.clone=function(){return new y(this.string(),this.forceY)},x.toggle=function(a,c){var e=this.cache,f=this.options,g=this.tooltip;if(c){if(/over|enter/.test(c.type)&&e.event&&/out|leave/.test(e.event.type)&&f.show.target.add(c.target).length===f.show.target.length&&g.has(c.relatedTarget).length)return this;e.event=d.event.fix(c)}if(this.waiting&&!a&&(this.hiddenDuringWait=B),!this.rendered)return a?this.render(1):this;if(this.destroyed||this.disabled)return this;var h,i,j,k=a?"show":"hide",l=this.options[k],m=(this.options[a?"hide":"show"],this.options.position),n=this.options.content,o=this.tooltip.css("width"),p=this.tooltip.is(":visible"),q=a||1===l.target.length,r=!c||l.target.length<2||e.target[0]===c.target;return(typeof a).search("boolean|number")&&(a=!p),h=!g.is(":animated")&&p===a&&r,i=h?D:!!this._trigger(k,[90]),this.destroyed?this:(i!==C&&a&&this.focus(c),!i||h?this:(d.attr(g[0],"aria-hidden",!a),a?(this.mouse&&(e.origin=d.event.fix(this.mouse)),d.isFunction(n.text)&&this._updateContent(n.text,C),d.isFunction(n.title)&&this._updateTitle(n.title,C),!A&&"mouse"===m.target&&m.adjust.mouse&&(d(b).bind("mousemove."+P,this._storeMouse),A=B),o||g.css("width",g.outerWidth(C)),this.reposition(c,arguments[2]),o||g.css("width",""),l.solo&&("string"==typeof l.solo?d(l.solo):d(T,l.solo)).not(g).not(l.target).qtip("hide",d.Event("tooltipsolo"))):(clearTimeout(this.timers.show),delete e.origin,A&&!d(T+'[tracking="true"]:visible',l.solo).not(g).length&&(d(b).unbind("mousemove."+P),A=C),this.blur(c)),j=d.proxy(function(){a?(ab.ie&&g[0].style.removeAttribute("filter"),g.css("overflow",""),"string"==typeof l.autofocus&&d(this.options.show.autofocus,g).focus(),this.options.show.target.trigger("qtip-"+this.id+"-inactive")):g.css({display:"",visibility:"",opacity:"",left:"",top:""}),this._trigger(a?"visible":"hidden")},this),l.effect===C||q===C?(g[k](),j()):d.isFunction(l.effect)?(g.stop(1,1),l.effect.call(g,this),g.queue("fx",function(a){j(),a()})):g.fadeTo(90,a?1:0,j),a&&l.target.trigger("qtip-"+this.id+"-inactive"),this))},x.show=function(a){return this.toggle(B,a)},x.hide=function(a){return this.toggle(C,a)},x.focus=function(a){if(!this.rendered||this.destroyed)return this;var b=d(T),c=this.tooltip,e=parseInt(c[0].style.zIndex,10),f=w.zindex+b.length;return c.hasClass(X)||this._trigger("focus",[f],a)&&(e!==f&&(b.each(function(){this.style.zIndex>e&&(this.style.zIndex=this.style.zIndex-1)}),b.filter("."+X).qtip("blur",a)),c.addClass(X)[0].style.zIndex=f),this},x.blur=function(a){return!this.rendered||this.destroyed?this:(this.tooltip.removeClass(X),this._trigger("blur",[this.tooltip.css("zIndex")],a),this)},x.disable=function(a){return this.destroyed?this:("toggle"===a?a=!(this.rendered?this.tooltip.hasClass(Z):this.disabled):"boolean"!=typeof a&&(a=B),this.rendered&&this.tooltip.toggleClass(Z,a).attr("aria-disabled",a),this.disabled=!!a,this)},x.enable=function(){return this.disable(C)},x._createButton=function(){var a=this,b=this.elements,c=b.tooltip,e=this.options.content.button,f="string"==typeof e,g=f?e:"Close tooltip";b.button&&b.button.remove(),b.button=e.jquery?e:d("<a />",{"class":"qtip-close "+(this.options.style.widget?"":P+"-icon"),title:g,"aria-label":g}).prepend(d("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),b.button.appendTo(b.titlebar||c).attr("role","button").click(function(b){return c.hasClass(Z)||a.hide(b),C})},x._updateButton=function(a){if(!this.rendered)return C;var b=this.elements.button;a?this._createButton():b.remove()},x._setWidget=function(){var a=this.options.style.widget,b=this.elements,c=b.tooltip,d=c.hasClass(Z);c.removeClass(Z),Z=a?"ui-state-disabled":"qtip-disabled",c.toggleClass(Z,d),c.toggleClass("ui-helper-reset "+k(),a).toggleClass(W,this.options.style.def&&!a),b.content&&b.content.toggleClass(k("content"),a),b.titlebar&&b.titlebar.toggleClass(k("header"),a),b.button&&b.button.toggleClass(P+"-icon",!a)},x._storeMouse=function(a){return(this.mouse=d.event.fix(a)).type="mousemove",this},x._bind=function(a,b,c,e,f){if(a&&c&&b.length){var g="."+this._id+(e?"-"+e:"");return d(a).bind((b.split?b:b.join(g+" "))+g,d.proxy(c,f||this)),this}},x._unbind=function(a,b){return a&&d(a).unbind("."+this._id+(b?"-"+b:"")),this},x._trigger=function(a,b,c){var e=d.Event("tooltip"+a);return e.originalEvent=c&&d.extend({},c)||this.cache.event||D,this.triggering=a,this.tooltip.trigger(e,[this].concat(b||[])),this.triggering=C,!e.isDefaultPrevented()},x._bindEvents=function(a,b,c,e,f,g){var h=c.filter(e).add(e.filter(c)),i=[];h.length&&(d.each(b,function(b,c){var e=d.inArray(c,a);e>-1&&i.push(a.splice(e,1)[0])}),i.length&&(this._bind(h,i,function(a){var b=this.rendered?this.tooltip[0].offsetWidth>0:!1;(b?g:f).call(this,a)}),c=c.not(h),e=e.not(h))),this._bind(c,a,f),this._bind(e,b,g)},x._assignInitialEvents=function(a){function b(a){return this.disabled||this.destroyed?C:(this.cache.event=a&&d.event.fix(a),this.cache.target=a&&d(a.target),clearTimeout(this.timers.show),void(this.timers.show=l.call(this,function(){this.render("object"==typeof a||c.show.ready)},c.prerender?0:c.show.delay)))}var c=this.options,e=c.show.target,f=c.hide.target,g=c.show.event?d.trim(""+c.show.event).split(" "):[],h=c.hide.event?d.trim(""+c.hide.event).split(" "):[];this._bind(this.elements.target,["remove","removeqtip"],function(){this.destroy(!0)},"destroy"),/mouse(over|enter)/i.test(c.show.event)&&!/mouse(out|leave)/i.test(c.hide.event)&&h.push("mouseleave"),this._bind(e,"mousemove",function(a){this._storeMouse(a),this.cache.onTarget=B}),this._bindEvents(g,h,e,f,b,function(){return this.timers?void clearTimeout(this.timers.show):C}),(c.show.ready||c.prerender)&&b.call(this,a)},x._assignEvents=function(){var c=this,e=this.options,f=e.position,g=this.tooltip,h=e.show.target,i=e.hide.target,j=f.container,k=f.viewport,l=d(b),q=(d(b.body),d(a)),r=e.show.event?d.trim(""+e.show.event).split(" "):[],s=e.hide.event?d.trim(""+e.hide.event).split(" "):[];d.each(e.events,function(a,b){c._bind(g,"toggle"===a?["tooltipshow","tooltiphide"]:["tooltip"+a],b,null,g)}),/mouse(out|leave)/i.test(e.hide.event)&&"window"===e.hide.leave&&this._bind(l,["mouseout","blur"],function(a){/select|option/.test(a.target.nodeName)||a.relatedTarget||this.hide(a)}),e.hide.fixed?i=i.add(g.addClass(V)):/mouse(over|enter)/i.test(e.show.event)&&this._bind(i,"mouseleave",function(){clearTimeout(this.timers.show)}),(""+e.hide.event).indexOf("unfocus")>-1&&this._bind(j.closest("html"),["mousedown","touchstart"],function(a){var b=d(a.target),c=this.rendered&&!this.tooltip.hasClass(Z)&&this.tooltip[0].offsetWidth>0,e=b.parents(T).filter(this.tooltip[0]).length>0;b[0]===this.target[0]||b[0]===this.tooltip[0]||e||this.target.has(b[0]).length||!c||this.hide(a)}),"number"==typeof e.hide.inactive&&(this._bind(h,"qtip-"+this.id+"-inactive",o,"inactive"),this._bind(i.add(g),w.inactiveEvents,o)),this._bindEvents(r,s,h,i,m,n),this._bind(h.add(g),"mousemove",function(a){if("number"==typeof e.hide.distance){var b=this.cache.origin||{},c=this.options.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&this.hide(a)}this._storeMouse(a)}),"mouse"===f.target&&f.adjust.mouse&&(e.hide.event&&this._bind(h,["mouseenter","mouseleave"],function(a){return this.cache?void(this.cache.onTarget="mouseenter"===a.type):C}),this._bind(l,"mousemove",function(a){this.rendered&&this.cache.onTarget&&!this.tooltip.hasClass(Z)&&this.tooltip[0].offsetWidth>0&&this.reposition(a)})),(f.adjust.resize||k.length)&&this._bind(d.event.special.resize?k:q,"resize",p),f.adjust.scroll&&this._bind(q.add(f.container),"scroll",p)},x._unassignEvents=function(){var c=this.options,e=c.show.target,f=c.hide.target,g=d.grep([this.elements.target[0],this.rendered&&this.tooltip[0],c.position.container[0],c.position.viewport[0],c.position.container.closest("html")[0],a,b],function(a){return"object"==typeof a});e&&e.toArray&&(g=g.concat(e.toArray())),f&&f.toArray&&(g=g.concat(f.toArray())),this._unbind(g)._unbind(g,"destroy")._unbind(g,"inactive")},d(function(){q(T,["mouseenter","mouseleave"],function(a){var b="mouseenter"===a.type,c=d(a.currentTarget),e=d(a.relatedTarget||a.target),f=this.options;b?(this.focus(a),c.hasClass(V)&&!c.hasClass(Z)&&clearTimeout(this.timers.hide)):"mouse"===f.position.target&&f.position.adjust.mouse&&f.hide.event&&f.show.target&&!e.closest(f.show.target[0]).length&&this.hide(a),c.toggleClass(Y,b)}),q("["+R+"]",U,o)}),w=d.fn.qtip=function(a,b,e){var f=(""+a).toLowerCase(),g=D,i=d.makeArray(arguments).slice(1),j=i[i.length-1],k=this[0]?d.data(this[0],P):D;return!arguments.length&&k||"api"===f?k:"string"==typeof a?(this.each(function(){var a=d.data(this,P);if(!a)return B;if(j&&j.timeStamp&&(a.cache.event=j),!b||"option"!==f&&"options"!==f)a[f]&&a[f].apply(a,i);else{if(e===c&&!d.isPlainObject(b))return g=a.get(b),C;a.set(b,e)}}),g!==D?g:this):"object"!=typeof a&&arguments.length?void 0:(k=h(d.extend(B,{},a)),this.each(function(a){var b,c;return c=d.isArray(k.id)?k.id[a]:k.id,c=!c||c===C||c.length<1||w.api[c]?w.nextid++:c,b=r(d(this),c,k),b===C?B:(w.api[c]=b,d.each(O,function(){"initialize"===this.initialize&&this(b)}),void b._assignInitialEvents(j))}))},d.qtip=e,w.api={},d.each({attr:function(a,b){if(this.length){var c=this[0],e="title",f=d.data(c,"qtip");if(a===e&&f&&"object"==typeof f&&f.options.suppress)return arguments.length<2?d.attr(c,_):(f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",b),this.attr(_,b))}return d.fn["attr"+$].apply(this,arguments)},clone:function(a){var b=(d([]),d.fn["clone"+$].apply(this,arguments));return a||b.filter("["+_+"]").attr("title",function(){return d.attr(this,_)}).removeAttr(_),b}},function(a,b){if(!b||d.fn[a+$])return B;var c=d.fn[a+$]=d.fn[a];d.fn[a]=function(){return b.apply(this,arguments)||c.apply(this,arguments)}}),d.ui||(d["cleanData"+$]=d.cleanData,d.cleanData=function(a){for(var b,c=0;(b=d(a[c])).length;c++)if(b.attr(Q))try{b.triggerHandler("removeqtip")}catch(e){}d["cleanData"+$].apply(this,arguments)}),w.version="2.2.1",w.nextid=0,w.inactiveEvents=U,w.zindex=15e3,w.defaults={prerender:C,id:C,overwrite:B,suppress:B,content:{text:B,attr:"title",title:C,button:C},position:{my:"top left",at:"bottom right",target:C,container:C,viewport:C,adjust:{x:0,y:0,mouse:B,scroll:B,resize:B,method:"flipinvert flipinvert"},effect:function(a,b){d(this).animate(b,{duration:200,queue:C})}},show:{target:C,event:"mouseenter",effect:B,delay:90,solo:C,ready:C,autofocus:C},hide:{target:C,event:"mouseleave",effect:B,delay:0,fixed:C,inactive:C,leave:"window",distance:C},style:{classes:"",widget:C,width:C,height:C,def:B},events:{render:D,move:D,show:D,hide:D,toggle:D,visible:D,hidden:D,focus:D,blur:D}};var eb,fb="margin",gb="border",hb="color",ib="background-color",jb="transparent",kb=" !important",lb=!!b.createElement("canvas").getContext,mb=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,nb={},ob=["Webkit","O","Moz","ms"];if(lb)var pb=a.devicePixelRatio||1,qb=function(){var a=b.createElement("canvas").getContext("2d");return a.backingStorePixelRatio||a.webkitBackingStorePixelRatio||a.mozBackingStorePixelRatio||a.msBackingStorePixelRatio||a.oBackingStorePixelRatio||1}(),rb=pb/qb;else var sb=function(a,b,c){return"<qtipvml:"+a+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(b||"")+' style="behavior: url(#default#VML); '+(c||"")+'" />'};d.extend(v.prototype,{init:function(a){var b,c;c=this.element=a.elements.tip=d("<div />",{"class":P+"-tip"}).prependTo(a.tooltip),lb?(b=d("<canvas />").appendTo(this.element)[0].getContext("2d"),b.lineJoin="miter",b.miterLimit=1e5,b.save()):(b=sb("shape",'coordorigin="0,0"',"position:absolute;"),this.element.html(b+b),a._bind(d("*",c).add(c),["click","mousedown"],function(a){a.stopPropagation()},this._ns)),a._bind(a.tooltip,"tooltipmove",this.reposition,this._ns,this),this.create()},_swapDimensions:function(){this.size[0]=this.options.height,this.size[1]=this.options.width},_resetDimensions:function(){this.size[0]=this.options.width,this.size[1]=this.options.height},_useTitle:function(a){var b=this.qtip.elements.titlebar;return b&&(a.y===I||a.y===M&&this.element.position().top+this.size[1]/2+this.options.offset<b.outerHeight(B))},_parseCorner:function(a){var b=this.qtip.options.position.my;return a===C||b===C?a=C:a===B?a=new y(b.string()):a.string||(a=new y(a),a.fixed=B),a},_parseWidth:function(a,b,c){var d=this.qtip.elements,e=gb+s(b)+"Width";return(c?u(c,e):u(d.content,e)||u(this._useTitle(a)&&d.titlebar||d.content,e)||u(d.tooltip,e))||0},_parseRadius:function(a){var b=this.qtip.elements,c=gb+s(a.y)+s(a.x)+"Radius";return ab.ie<9?0:u(this._useTitle(a)&&b.titlebar||b.content,c)||u(b.tooltip,c)||0},_invalidColour:function(a,b,c){var d=a.css(b);return!d||c&&d===a.css(c)||mb.test(d)?C:d},_parseColours:function(a){var b=this.qtip.elements,c=this.element.css("cssText",""),e=gb+s(a[a.precedance])+s(hb),f=this._useTitle(a)&&b.titlebar||b.content,g=this._invalidColour,h=[];return h[0]=g(c,ib)||g(f,ib)||g(b.content,ib)||g(b.tooltip,ib)||c.css(ib),h[1]=g(c,e,hb)||g(f,e,hb)||g(b.content,e,hb)||g(b.tooltip,e,hb)||b.tooltip.css(e),d("*",c).add(c).css("cssText",ib+":"+jb+kb+";"+gb+":0"+kb+";"),h},_calculateSize:function(a){var b,c,d,e=a.precedance===F,f=this.options.width,g=this.options.height,h="c"===a.abbrev(),i=(e?f:g)*(h?.5:1),j=Math.pow,k=Math.round,l=Math.sqrt(j(i,2)+j(g,2)),m=[this.border/i*l,this.border/g*l];return m[2]=Math.sqrt(j(m[0],2)-j(this.border,2)),m[3]=Math.sqrt(j(m[1],2)-j(this.border,2)),b=l+m[2]+m[3]+(h?0:m[0]),c=b/l,d=[k(c*f),k(c*g)],e?d:d.reverse()},_calculateTip:function(a,b,c){c=c||1,b=b||this.size;var d=b[0]*c,e=b[1]*c,f=Math.ceil(d/2),g=Math.ceil(e/2),h={br:[0,0,d,e,d,0],bl:[0,0,d,0,0,e],tr:[0,e,d,0,d,e],tl:[0,0,0,e,d,e],tc:[0,e,f,0,d,e],bc:[0,0,d,0,f,e],rc:[0,0,d,g,0,e],lc:[d,0,d,e,0,g]};return h.lt=h.br,h.rt=h.bl,h.lb=h.tr,h.rb=h.tl,h[a.abbrev()]},_drawCoords:function(a,b){a.beginPath(),a.moveTo(b[0],b[1]),a.lineTo(b[2],b[3]),a.lineTo(b[4],b[5]),a.closePath()},create:function(){var a=this.corner=(lb||ab.ie)&&this._parseCorner(this.options.corner);return(this.enabled=!!this.corner&&"c"!==this.corner.abbrev())&&(this.qtip.cache.corner=a.clone(),this.update()),this.element.toggle(this.enabled),this.corner},update:function(b,c){if(!this.enabled)return this;var e,f,g,h,i,j,k,l,m=this.qtip.elements,n=this.element,o=n.children(),p=this.options,q=this.size,r=p.mimic,s=Math.round;b||(b=this.qtip.cache.corner||this.corner),r===C?r=b:(r=new y(r),r.precedance=b.precedance,"inherit"===r.x?r.x=b.x:"inherit"===r.y?r.y=b.y:r.x===r.y&&(r[b.precedance]=b[b.precedance])),f=r.precedance,b.precedance===E?this._swapDimensions():this._resetDimensions(),e=this.color=this._parseColours(b),e[1]!==jb?(l=this.border=this._parseWidth(b,b[b.precedance]),p.border&&1>l&&!mb.test(e[1])&&(e[0]=e[1]),this.border=l=p.border!==B?p.border:l):this.border=l=0,k=this.size=this._calculateSize(b),n.css({width:k[0],height:k[1],lineHeight:k[1]+"px"}),j=b.precedance===F?[s(r.x===J?l:r.x===L?k[0]-q[0]-l:(k[0]-q[0])/2),s(r.y===I?k[1]-q[1]:0)]:[s(r.x===J?k[0]-q[0]:0),s(r.y===I?l:r.y===K?k[1]-q[1]-l:(k[1]-q[1])/2)],lb?(g=o[0].getContext("2d"),g.restore(),g.save(),g.clearRect(0,0,6e3,6e3),h=this._calculateTip(r,q,rb),i=this._calculateTip(r,this.size,rb),o.attr(G,k[0]*rb).attr(H,k[1]*rb),o.css(G,k[0]).css(H,k[1]),this._drawCoords(g,i),g.fillStyle=e[1],g.fill(),g.translate(j[0]*rb,j[1]*rb),this._drawCoords(g,h),g.fillStyle=e[0],g.fill()):(h=this._calculateTip(r),h="m"+h[0]+","+h[1]+" l"+h[2]+","+h[3]+" "+h[4]+","+h[5]+" xe",j[2]=l&&/^(r|b)/i.test(b.string())?8===ab.ie?2:1:0,o.css({coordsize:k[0]+l+" "+(k[1]+l),antialias:""+(r.string().indexOf(M)>-1),left:j[0]-j[2]*Number(f===E),top:j[1]-j[2]*Number(f===F),width:k[0]+l,height:k[1]+l}).each(function(a){var b=d(this);b[b.prop?"prop":"attr"]({coordsize:k[0]+l+" "+(k[1]+l),path:h,fillcolor:e[0],filled:!!a,stroked:!a}).toggle(!(!l&&!a)),!a&&b.html(sb("stroke",'weight="'+2*l+'px" color="'+e[1]+'" miterlimit="1000" joinstyle="miter"'))})),a.opera&&setTimeout(function(){m.tip.css({display:"inline-block",visibility:"visible"})},1),c!==C&&this.calculate(b,k)},calculate:function(a,b){if(!this.enabled)return C;var c,e,f=this,g=this.qtip.elements,h=this.element,i=this.options.offset,j=(g.tooltip.hasClass("ui-widget"),{});return a=a||this.corner,c=a.precedance,b=b||this._calculateSize(a),e=[a.x,a.y],c===E&&e.reverse(),d.each(e,function(d,e){var h,k,l;e===M?(h=c===F?J:I,j[h]="50%",j[fb+"-"+h]=-Math.round(b[c===F?0:1]/2)+i):(h=f._parseWidth(a,e,g.tooltip),k=f._parseWidth(a,e,g.content),l=f._parseRadius(a),j[e]=Math.max(-f.border,d?k:i+(l>h?l:-h)))
}),j[a[c]]-=b[c===E?0:1],h.css({margin:"",top:"",bottom:"",left:"",right:""}).css(j),j},reposition:function(a,b,d){function e(a,b,c,d,e){a===N&&j.precedance===b&&k[d]&&j[c]!==M?j.precedance=j.precedance===E?F:E:a!==N&&k[d]&&(j[b]=j[b]===M?k[d]>0?d:e:j[b]===d?e:d)}function f(a,b,e){j[a]===M?p[fb+"-"+b]=o[a]=g[fb+"-"+b]-k[b]:(h=g[e]!==c?[k[b],-g[b]]:[-k[b],g[b]],(o[a]=Math.max(h[0],h[1]))>h[0]&&(d[b]-=k[b],o[b]=C),p[g[e]!==c?e:b]=o[a])}if(this.enabled){var g,h,i=b.cache,j=this.corner.clone(),k=d.adjusted,l=b.options.position.adjust.method.split(" "),m=l[0],n=l[1]||l[0],o={left:C,top:C,x:0,y:0},p={};this.corner.fixed!==B&&(e(m,E,F,J,L),e(n,F,E,I,K),(j.string()!==i.corner.string()||i.cornerTop!==k.top||i.cornerLeft!==k.left)&&this.update(j,C)),g=this.calculate(j),g.right!==c&&(g.left=-g.right),g.bottom!==c&&(g.top=-g.bottom),g.user=this.offset,(o.left=m===N&&!!k.left)&&f(E,J,L),(o.top=n===N&&!!k.top)&&f(F,I,K),this.element.css(p).toggle(!(o.x&&o.y||j.x===M&&o.y||j.y===M&&o.x)),d.left-=g.left.charAt?g.user:m!==N||o.top||!o.left&&!o.top?g.left+this.border:0,d.top-=g.top.charAt?g.user:n!==N||o.left||!o.left&&!o.top?g.top+this.border:0,i.cornerLeft=k.left,i.cornerTop=k.top,i.corner=j.clone()}},destroy:function(){this.qtip._unbind(this.qtip.tooltip,this._ns),this.qtip.elements.tip&&this.qtip.elements.tip.find("*").remove().end().remove()}}),eb=O.tip=function(a){return new v(a,a.options.style.tip)},eb.initialize="render",eb.sanitize=function(a){if(a.style&&"tip"in a.style){var b=a.style.tip;"object"!=typeof b&&(b=a.style.tip={corner:b}),/string|boolean/i.test(typeof b.corner)||(b.corner=B)}},z.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){this.create(),this.qtip.reposition()},"^style.tip.(height|width)$":function(a){this.size=[a.width,a.height],this.update(),this.qtip.reposition()},"^content.title|style.(classes|widget)$":function(){this.update()}},d.extend(B,w.defaults,{style:{tip:{corner:B,mimic:C,width:6,height:6,border:B,offset:0}}})})}(window,document);
//# sourceMappingURL=jquery.qtip.min.js.map;
/*!
 * jQuery UI Touch Punch 0.2.2
 *
 * Copyright 2011, Dave Furfero
 * Dual licensed under the MIT or GPL Version 2 licenses.
 *
 * Depends:
 *  jquery.ui.widget.js
 *  jquery.ui.mouse.js
 */
(function ($) {

  // Detect touch support
  $.support.touch = 'ontouchend' in document;

  // Ignore browsers without touch support
  if (!$.support.touch) {
    return;
  }

  var mouseProto = $.ui.mouse.prototype,
      _mouseInit = mouseProto._mouseInit,
      touchHandled;

  /**
   * Simulate a mouse event based on a corresponding touch event
   * @param {Object} event A touch event
   * @param {String} simulatedType The corresponding mouse event
   */
  function simulateMouseEvent (event, simulatedType) {

    // Ignore multi-touch events
    if (event.originalEvent.touches.length > 1) {
      return;
    }

    event.preventDefault();

    var touch = event.originalEvent.changedTouches[0],
        simulatedEvent = document.createEvent('MouseEvents');
    
    // Initialize the simulated mouse event using the touch event's coordinates
    simulatedEvent.initMouseEvent(
      simulatedType,    // type
      true,             // bubbles                    
      true,             // cancelable                 
      window,           // view                       
      1,                // detail                     
      touch.screenX,    // screenX                    
      touch.screenY,    // screenY                    
      touch.clientX,    // clientX                    
      touch.clientY,    // clientY                    
      false,            // ctrlKey                    
      false,            // altKey                     
      false,            // shiftKey                   
      false,            // metaKey                    
      0,                // button                     
      null              // relatedTarget              
    );

    // Dispatch the simulated event to the target element
    event.target.dispatchEvent(simulatedEvent);
  }

  /**
   * Handle the jQuery UI widget's touchstart events
   * @param {Object} event The widget element's touchstart event
   */
  mouseProto._touchStart = function (event) {

    var self = this;

    // Ignore the event if another widget is already being handled
    if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) {
      return;
    }

    // Set the flag to prevent other widgets from inheriting the touch event
    touchHandled = true;

    // Track movement to determine if interaction was a click
    self._touchMoved = false;

    // Simulate the mouseover event
    simulateMouseEvent(event, 'mouseover');

    // Simulate the mousemove event
    simulateMouseEvent(event, 'mousemove');

    // Simulate the mousedown event
    simulateMouseEvent(event, 'mousedown');
  };

  /**
   * Handle the jQuery UI widget's touchmove events
   * @param {Object} event The document's touchmove event
   */
  mouseProto._touchMove = function (event) {

    // Ignore event if not handled
    if (!touchHandled) {
      return;
    }

    // Interaction was not a click
    this._touchMoved = true;

    // Simulate the mousemove event
    simulateMouseEvent(event, 'mousemove');
  };

  /**
   * Handle the jQuery UI widget's touchend events
   * @param {Object} event The document's touchend event
   */
  mouseProto._touchEnd = function (event) {

    // Ignore event if not handled
    if (!touchHandled) {
      return;
    }

    // Simulate the mouseup event
    simulateMouseEvent(event, 'mouseup');

    // Simulate the mouseout event
    simulateMouseEvent(event, 'mouseout');

    // If the touch interaction did not move, it should trigger a click
    if (!this._touchMoved) {

      // Simulate the click event
      simulateMouseEvent(event, 'click');
    }

    // Unset the flag to allow other widgets to inherit the touch event
    touchHandled = false;
  };

  /**
   * A duck punch of the $.ui.mouse _mouseInit method to support touch events.
   * This method extends the widget with bound touch event handlers that
   * translate touch events to mouse events and pass them to the widget's
   * original mouse event handling methods.
   */
  mouseProto._mouseInit = function () {
    
    var self = this;

    // Delegate the touch handlers to the widget's element
    self.element
      .bind('touchstart', $.proxy(self, '_touchStart'))
      .bind('touchmove', $.proxy(self, '_touchMove'))
      .bind('touchend', $.proxy(self, '_touchEnd'));

    // Call the original $.ui.mouse init method
    _mouseInit.call(self);
  };

})(jQuery);;
//ZG - Customized to change default tab behavior that was not 508 compatible


/*
 * typeahead.js 0.11.1
 * https://github.com/twitter/typeahead.js
 * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT
 */

(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define("bloodhound", ["jquery"], function (a0) {
            return root["Bloodhound"] = factory(a0);
        });
    } else if (typeof exports === "object") {
        module.exports = factory(require("jquery"));
    } else {
        root["Bloodhound"] = factory(jQuery);
    }
})(this, function ($) {
    var _ = function () {
        "use strict";
        return {
            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";
            },
            isElement: function (obj) {
                return !!(obj && obj.nodeType === 1);
            },
            isJQuery: function (obj) {
                return obj instanceof $;
            },
            toStr: function toStr(s) {
                return _.isUndefined(s) || s === null ? "" : s + "";
            },
            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,
            identity: function (x) {
                return x;
            },
            clone: function (obj) {
                return $.extend(true, {}, obj);
            },
            getIdGenerator: 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;
                };
            },
            stringify: function (val) {
                return _.isString(val) ? val : JSON.stringify(val);
            },
            noop: function () { }
        };
    }();
    var VERSION = "0.11.1";
    var tokenizers = function () {
        "use strict";
        return {
            nonword: nonword,
            whitespace: whitespace,
            obj: {
                nonword: getObjTokenizer(nonword),
                whitespace: getObjTokenizer(whitespace)
            }
        };
        function whitespace(str) {
            str = _.toStr(str);
            return str ? str.split(/\s+/) : [];
        }
        function nonword(str) {
            str = _.toStr(str);
            return str ? str.split(/\W+/) : [];
        }
        function getObjTokenizer(tokenizer) {
            return function setKey(keys) {
                keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0);
                return function tokenize(o) {
                    var tokens = [];
                    _.each(keys, function (k) {
                        tokens = tokens.concat(tokenizer(_.toStr(o[k])));
                    });
                    return tokens;
                };
            };
        }
    }();
    var LruCache = function () {
        "use strict";
        function LruCache(maxSize) {
            this.maxSize = _.isNumber(maxSize) ? maxSize : 100;
            this.reset();
            if (this.maxSize <= 0) {
                this.set = this.get = $.noop;
            }
        }
        _.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];
                    this.size--;
                }
                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;
                }
            },
            reset: function reset() {
                this.size = 0;
                this.hash = {};
                this.list = new List();
            }
        });
        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 () {
        "use strict";
        var LOCAL_STORAGE;
        try {
            LOCAL_STORAGE = window.localStorage;
            LOCAL_STORAGE.setItem("~~~", "!");
            LOCAL_STORAGE.removeItem("~~~");
        } catch (err) {
            LOCAL_STORAGE = null;
        }
        function PersistentStorage(namespace, override) {
            this.prefix = ["__", namespace, "__"].join("");
            this.ttlKey = "__ttl__";
            this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix));
            this.ls = override || LOCAL_STORAGE;
            !this.ls && this._noop();
        }
        _.mixin(PersistentStorage.prototype, {
            _prefix: function (key) {
                return this.prefix + key;
            },
            _ttlKey: function (key) {
                return this._prefix(key) + this.ttlKey;
            },
            _noop: function () {
                this.get = this.set = this.remove = this.clear = this.isExpired = _.noop;
            },
            _safeSet: function (key, val) {
                try {
                    this.ls.setItem(key, val);
                } catch (err) {
                    if (err.name === "QuotaExceededError") {
                        this.clear();
                        this._noop();
                    }
                }
            },
            get: function (key) {
                if (this.isExpired(key)) {
                    this.remove(key);
                }
                return decode(this.ls.getItem(this._prefix(key)));
            },
            set: function (key, val, ttl) {
                if (_.isNumber(ttl)) {
                    this._safeSet(this._ttlKey(key), encode(now() + ttl));
                } else {
                    this.ls.removeItem(this._ttlKey(key));
                }
                return this._safeSet(this._prefix(key), encode(val));
            },
            remove: function (key) {
                this.ls.removeItem(this._ttlKey(key));
                this.ls.removeItem(this._prefix(key));
                return this;
            },
            clear: function () {
                var i, keys = gatherMatchingKeys(this.keyMatcher);
                for (i = keys.length; i--;) {
                    this.remove(keys[i]);
                }
                return this;
            },
            isExpired: function (key) {
                var ttl = decode(this.ls.getItem(this._ttlKey(key)));
                return _.isNumber(ttl) && now() > ttl ? true : false;
            }
        });
        return PersistentStorage;
        function now() {
            return new Date().getTime();
        }
        function encode(val) {
            return JSON.stringify(_.isUndefined(val) ? null : val);
        }
        function decode(val) {
            return $.parseJSON(val);
        }
        function gatherMatchingKeys(keyMatcher) {
            var i, key, keys = [], len = LOCAL_STORAGE.length;
            for (i = 0; i < len; i++) {
                if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) {
                    keys.push(key.replace(keyMatcher, ""));
                }
            }
            return keys;
        }
    }();
    var Transport = function () {
        "use strict";
        var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10);
        function Transport(o) {
            o = o || {};
            this.cancelled = false;
            this.lastReq = null;
            this._send = o.transport;
            this._get = o.limiter ? o.limiter(this._get) : this._get;
            this._cache = o.cache === false ? new LruCache(0) : sharedCache;
        }
        Transport.setMaxPendingRequests = function setMaxPendingRequests(num) {
            maxPendingRequests = num;
        };
        Transport.resetCache = function resetCache() {
            sharedCache.reset();
        };
        _.mixin(Transport.prototype, {
            _fingerprint: function fingerprint(o) {
                o = o || {};
                return o.url + o.type + $.param(o.data || {});
            },
            _get: function (o, cb) {
                var that = this, fingerprint, jqXhr;
                fingerprint = this._fingerprint(o);
                if (this.cancelled || fingerprint !== this.lastReq) {
                    return;
                }
                if (jqXhr = pendingRequests[fingerprint]) {
                    jqXhr.done(done).fail(fail);
                } else if (pendingRequestsCount < maxPendingRequests) {
                    pendingRequestsCount++;
                    pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always);
                } else {
                    this.onDeckRequestArgs = [].slice.call(arguments, 0);
                }
                function done(resp) {
                    cb(null, resp);
                    that._cache.set(fingerprint, resp);
                }
                function fail() {
                    cb(true);
                }
                function always() {
                    pendingRequestsCount--;
                    delete pendingRequests[fingerprint];
                    if (that.onDeckRequestArgs) {
                        that._get.apply(that, that.onDeckRequestArgs);
                        that.onDeckRequestArgs = null;
                    }
                }
            },
            get: function (o, cb) {
                var resp, fingerprint;
                cb = cb || $.noop;
                o = _.isString(o) ? {
                    url: o
                } : o || {};
                fingerprint = this._fingerprint(o);
                this.cancelled = false;
                this.lastReq = fingerprint;
                if (resp = this._cache.get(fingerprint)) {
                    cb(null, resp);
                } else {
                    this._get(o, cb);
                }
            },
            cancel: function () {
                this.cancelled = true;
            }
        });
        return Transport;
    }();
    var SearchIndex = window.SearchIndex = function () {
        "use strict";
        var CHILDREN = "c", IDS = "i";
        function SearchIndex(o) {
            o = o || {};
            if (!o.datumTokenizer || !o.queryTokenizer) {
                $.error("datumTokenizer and queryTokenizer are both required");
            }
            this.identify = o.identify || _.stringify;
            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;
                    that.datums[id = that.identify(datum)] = datum;
                    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(ids) {
                var that = this;
                return _.map(ids, function (id) {
                    return that.datums[id];
                });
            },
            search: function search(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];
                }) : [];
            },
            all: function all() {
                var values = [];
                for (var key in this.datums) {
                    values.push(this.datums[key]);
                }
                return values;
            },
            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() {
            var node = {};
            node[IDS] = [];
            node[CHILDREN] = {};
            return node;
        }
        function unique(array) {
            var seen = {}, uniques = [];
            for (var i = 0, len = array.length; i < len; 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();
            arrayB = arrayB.sort();
            var lenArrayA = arrayA.length, lenArrayB = arrayB.length;
            while (ai < lenArrayA && bi < lenArrayB) {
                if (arrayA[ai] < arrayB[bi]) {
                    ai++;
                } else if (arrayA[ai] > arrayB[bi]) {
                    bi++;
                } else {
                    intersection.push(arrayA[ai]);
                    ai++;
                    bi++;
                }
            }
            return intersection;
        }
    }();
    var Prefetch = function () {
        "use strict";
        var keys;
        keys = {
            data: "data",
            protocol: "protocol",
            thumbprint: "thumbprint"
        };
        function Prefetch(o) {
            this.url = o.url;
            this.ttl = o.ttl;
            this.cache = o.cache;
            this.prepare = o.prepare;
            this.transform = o.transform;
            this.transport = o.transport;
            this.thumbprint = o.thumbprint;
            this.storage = new PersistentStorage(o.cacheKey);
        }
        _.mixin(Prefetch.prototype, {
            _settings: function settings() {
                return {
                    url: this.url,
                    type: "GET",
                    dataType: "json"
                };
            },
            store: function store(data) {
                if (!this.cache) {
                    return;
                }
                this.storage.set(keys.data, data, this.ttl);
                this.storage.set(keys.protocol, location.protocol, this.ttl);
                this.storage.set(keys.thumbprint, this.thumbprint, this.ttl);
            },
            fromCache: function fromCache() {
                var stored = {}, isExpired;
                if (!this.cache) {
                    return null;
                }
                stored.data = this.storage.get(keys.data);
                stored.protocol = this.storage.get(keys.protocol);
                stored.thumbprint = this.storage.get(keys.thumbprint);
                isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol;
                return stored.data && !isExpired ? stored.data : null;
            },
            fromNetwork: function (cb) {
                var that = this, settings;
                if (!cb) {
                    return;
                }
                settings = this.prepare(this._settings());
                this.transport(settings).fail(onError).done(onResponse);
                function onError() {
                    cb(true);
                }
                function onResponse(resp) {
                    cb(null, that.transform(resp));
                }
            },
            clear: function clear() {
                this.storage.clear();
                return this;
            }
        });
        return Prefetch;
    }();
    var Remote = function () {
        "use strict";
        function Remote(o) {
            this.url = o.url;
            this.prepare = o.prepare;
            this.transform = o.transform;
            this.transport = new Transport({
                cache: o.cache,
                limiter: o.limiter,
                transport: o.transport
            });
        }
        _.mixin(Remote.prototype, {
            _settings: function settings() {
                return {
                    url: this.url,
                    type: "GET",
                    dataType: "json"
                };
            },
            get: function get(query, cb) {
                var that = this, settings;
                if (!cb) {
                    return;
                }
                query = query || "";
                settings = this.prepare(query, this._settings());
                return this.transport.get(settings, onResponse);
                function onResponse(err, resp) {
                    err ? cb([]) : cb(that.transform(resp));
                }
            },
            cancelLastRequest: function cancelLastRequest() {
                this.transport.cancel();
            }
        });
        return Remote;
    }();
    var oParser = function () {
        "use strict";
        return function parse(o) {
            var defaults, sorter;
            defaults = {
                initialize: true,
                identify: _.stringify,
                datumTokenizer: null,
                queryTokenizer: null,
                sufficient: 5,
                sorter: null,
                local: [],
                prefetch: null,
                remote: null
            };
            o = _.mixin(defaults, o || {});
            !o.datumTokenizer && $.error("datumTokenizer is required");
            !o.queryTokenizer && $.error("queryTokenizer is required");
            sorter = o.sorter;
            o.sorter = sorter ? function (x) {
                return x.sort(sorter);
            } : _.identity;
            o.local = _.isFunction(o.local) ? o.local() : o.local;
            o.prefetch = parsePrefetch(o.prefetch);
            o.remote = parseRemote(o.remote);
            return o;
        };
        function parsePrefetch(o) {
            var defaults;
            if (!o) {
                return null;
            }
            defaults = {
                url: null,
                ttl: 24 * 60 * 60 * 1e3,
                cache: true,
                cacheKey: null,
                thumbprint: "",
                prepare: _.identity,
                transform: _.identity,
                transport: null
            };
            o = _.isString(o) ? {
                url: o
            } : o;
            o = _.mixin(defaults, o);
            !o.url && $.error("prefetch requires url to be set");
            o.transform = o.filter || o.transform;
            o.cacheKey = o.cacheKey || o.url;
            o.thumbprint = VERSION + o.thumbprint;
            o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
            return o;
        }
        function parseRemote(o) {
            var defaults;
            if (!o) {
                return;
            }
            defaults = {
                url: null,
                cache: true,
                prepare: null,
                replace: null,
                wildcard: null,
                limiter: null,
                rateLimitBy: "debounce",
                rateLimitWait: 300,
                transform: _.identity,
                transport: null
            };
            o = _.isString(o) ? {
                url: o
            } : o;
            o = _.mixin(defaults, o);
            !o.url && $.error("remote requires url to be set");
            o.transform = o.filter || o.transform;
            o.prepare = toRemotePrepare(o);
            o.limiter = toLimiter(o);
            o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax;
            delete o.replace;
            delete o.wildcard;
            delete o.rateLimitBy;
            delete o.rateLimitWait;
            return o;
        }
        function toRemotePrepare(o) {
            var prepare, replace, wildcard;
            prepare = o.prepare;
            replace = o.replace;
            wildcard = o.wildcard;
            if (prepare) {
                return prepare;
            }
            if (replace) {
                prepare = prepareByReplace;
            } else if (o.wildcard) {
                prepare = prepareByWildcard;
            } else {
                prepare = idenityPrepare;
            }
            return prepare;
            function prepareByReplace(query, settings) {
                settings.url = replace(settings.url, query);
                return settings;
            }
            function prepareByWildcard(query, settings) {
                settings.url = settings.url.replace(wildcard, encodeURIComponent(query));
                return settings;
            }
            function idenityPrepare(query, settings) {
                return settings;
            }
        }
        function toLimiter(o) {
            var limiter, method, wait;
            limiter = o.limiter;
            method = o.rateLimitBy;
            wait = o.rateLimitWait;
            if (!limiter) {
                limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait);
            }
            return limiter;
            function debounce(wait) {
                return function debounce(fn) {
                    return _.debounce(fn, wait);
                };
            }
            function throttle(wait) {
                return function throttle(fn) {
                    return _.throttle(fn, wait);
                };
            }
        }
        function callbackToDeferred(fn) {
            return function wrapper(o) {
                var deferred = $.Deferred();
                fn(o, onSuccess, onError);
                return deferred;
                function onSuccess(resp) {
                    _.defer(function () {
                        deferred.resolve(resp);
                    });
                }
                function onError(err) {
                    _.defer(function () {
                        deferred.reject(err);
                    });
                }
            };
        }
    }();
    var Bloodhound = function () {
        "use strict";
        var old;
        old = window && window.Bloodhound;
        function Bloodhound(o) {
            o = oParser(o);
            this.sorter = o.sorter;
            this.identify = o.identify;
            this.sufficient = o.sufficient;
            this.local = o.local;
            this.remote = o.remote ? new Remote(o.remote) : null;
            this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null;
            this.index = new SearchIndex({
                identify: this.identify,
                datumTokenizer: o.datumTokenizer,
                queryTokenizer: o.queryTokenizer
            });
            o.initialize !== false && this.initialize();
        }
        Bloodhound.noConflict = function noConflict() {
            window && (window.Bloodhound = old);
            return Bloodhound;
        };
        Bloodhound.tokenizers = tokenizers;
        _.mixin(Bloodhound.prototype, {
            __ttAdapter: function ttAdapter() {
                var that = this;
                return this.remote ? withAsync : withoutAsync;
                function withAsync(query, sync, async) {
                    return that.search(query, sync, async);
                }
                function withoutAsync(query, sync) {
                    return that.search(query, sync);
                }
            },
            _loadPrefetch: function loadPrefetch() {
                var that = this, deferred, serialized;
                deferred = $.Deferred();
                if (!this.prefetch) {
                    deferred.resolve();
                } else if (serialized = this.prefetch.fromCache()) {
                    this.index.bootstrap(serialized);
                    deferred.resolve();
                } else {
                    this.prefetch.fromNetwork(done);
                }
                return deferred.promise();
                function done(err, data) {
                    if (err) {
                        return deferred.reject();
                    }
                    that.add(data);
                    that.prefetch.store(that.index.serialize());
                    deferred.resolve();
                }
            },
            _initialize: function initialize() {
                var that = this, deferred;
                this.clear();
                (this.initPromise = this._loadPrefetch()).done(addLocalToIndex);
                return this.initPromise;
                function addLocalToIndex() {
                    that.add(that.local);
                }
            },
            initialize: function initialize(force) {
                return !this.initPromise || force ? this._initialize() : this.initPromise;
            },
            add: function add(data) {
                this.index.add(data);
                return this;
            },
            get: function get(ids) {
                ids = _.isArray(ids) ? ids : [].slice.call(arguments);
                return this.index.get(ids);
            },
            search: function search(query, sync, async) {
                var that = this, local;
                local = this.sorter(this.index.search(query));
                sync(this.remote ? local.slice() : local);
                if (this.remote && local.length < this.sufficient) {
                    this.remote.get(query, processRemote);
                } else if (this.remote) {
                    this.remote.cancelLastRequest();
                }
                return this;
                function processRemote(remote) {
                    var nonDuplicates = [];
                    _.each(remote, function (r) {
                        !_.some(local, function (l) {
                            return that.identify(r) === that.identify(l);
                        }) && nonDuplicates.push(r);
                    });
                    async && async(nonDuplicates);
                }
            },
            all: function all() {
                return this.index.all();
            },
            clear: function clear() {
                this.index.reset();
                return this;
            },
            clearPrefetchCache: function clearPrefetchCache() {
                this.prefetch && this.prefetch.clear();
                return this;
            },
            clearRemoteCache: function clearRemoteCache() {
                Transport.resetCache();
                return this;
            },
            ttAdapter: function ttAdapter() {
                return this.__ttAdapter();
            }
        });
        return Bloodhound;
    }();
    return Bloodhound;
});

(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define("typeahead.js", ["jquery"], function (a0) {
            return factory(a0);
        });
    } else if (typeof exports === "object") {
        module.exports = factory(require("jquery"));
    } else {
        factory(jQuery);
    }
})(this, function ($) {
    var _ = function () {
        "use strict";
        return {
            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";
            },
            isElement: function (obj) {
                return !!(obj && obj.nodeType === 1);
            },
            isJQuery: function (obj) {
                return obj instanceof $;
            },
            toStr: function toStr(s) {
                return _.isUndefined(s) || s === null ? "" : s + "";
            },
            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,
            identity: function (x) {
                return x;
            },
            clone: function (obj) {
                return $.extend(true, {}, obj);
            },
            getIdGenerator: 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;
                };
            },
            stringify: function (val) {
                return _.isString(val) ? val : JSON.stringify(val);
            },
            noop: function () { }
        };
    }();
    var WWW = function () {
        "use strict";
        var defaultClassNames = {
            wrapper: "twitter-typeahead",
            input: "tt-input",
            hint: "tt-hint",
            menu: "tt-menu",
            dataset: "tt-dataset",
            suggestion: "tt-suggestion",
            selectable: "tt-selectable",
            empty: "tt-empty",
            open: "tt-open",
            cursor: "tt-cursor",
            highlight: "tt-highlight"
        };
        return build;
        function build(o) {
            var www, classes;
            classes = _.mixin({}, defaultClassNames, o);
            www = {
                css: buildCss(),
                classes: classes,
                html: buildHtml(classes),
                selectors: buildSelectors(classes)
            };
            return {
                css: www.css,
                html: www.html,
                classes: www.classes,
                selectors: www.selectors,
                mixin: function (o) {
                    _.mixin(o, www);
                }
            };
        }
        function buildHtml(c) {
            return {
                wrapper: '<span class="' + c.wrapper + '"></span>',
                menu: '<div class="' + c.menu + '"></div>'
            };
        }
        function buildSelectors(classes) {
            var selectors = {};
            _.each(classes, function (v, k) {
                selectors[k] = "." + v;
            });
            return selectors;
        }
        function buildCss() {
            var css = {
                wrapper: {
                    position: "relative",
                },
                hint: {
                    position: "absolute",
                    top: "0",
                    left: "0",
                    borderColor: "transparent",
                    boxShadow: "none",
                    opacity: "1"
                },
                input: {
                    position: "relative",
                    verticalAlign: "top",
                    backgroundColor: "transparent"
                },
                inputWithNoHint: {
                    position: "relative",
                    verticalAlign: "top"
                },
                menu: {
                    position: "absolute",
                    top: "100%",
                    left: "0",
                    zIndex: "100",
                    display: "none"
                },
                ltr: {
                    left: "0",
                    right: "auto"
                },
                rtl: {
                    left: "auto",
                    right: " 0"
                }
            };
            if (_.isMsie()) {
                _.mixin(css.input, {
                    backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
                });
            }
            return css;
        }
    }();
    var EventBus = function () {
        "use strict";
        var namespace, deprecationMap;
        namespace = "typeahead:";
        deprecationMap = {
            render: "rendered",
            cursorchange: "cursorchanged",
            select: "selected",
            autocomplete: "autocompleted"
        };
        function EventBus(o) {
            if (!o || !o.el) {
                $.error("EventBus initialized without el");
            }
            this.$el = $(o.el);
        }
        _.mixin(EventBus.prototype, {
            _trigger: function (type, args) {
                var $e;
                $e = $.Event(namespace + type);
                (args = args || []).unshift($e);
                this.$el.trigger.apply(this.$el, args);
                return $e;
            },
            before: function (type) {
                var args, $e;
                args = [].slice.call(arguments, 1);
                $e = this._trigger("before" + type, args);
                return $e.isDefaultPrevented();
            },
            trigger: function (type) {
                var deprecatedType;
                this._trigger(type, [].slice.call(arguments, 1));
                if (deprecatedType = deprecationMap[type]) {
                    this._trigger(deprecatedType, [].slice.call(arguments, 1));
                }
            }
        });
        return EventBus;
    }();
    var EventEmitter = function () {
        "use strict";
        var splitter = /\s+/, nextTick = getNextTick();
        return {
            onSync: onSync,
            onAsync: onAsync,
            off: off,
            trigger: trigger
        };
        function on(method, types, cb, context) {
            var type;
            if (!cb) {
                return this;
            }
            types = types.split(splitter);
            cb = context ? bindContext(cb, context) : cb;
            this._callbacks = this._callbacks || {};
            while (type = types.shift()) {
                this._callbacks[type] = this._callbacks[type] || {
                    sync: [],
                    async: []
                };
                this._callbacks[type][method].push(cb);
            }
            return this;
        }
        function onAsync(types, cb, context) {
            return on.call(this, "async", types, cb, context);
        }
        function onSync(types, cb, context) {
            return on.call(this, "sync", types, cb, context);
        }
        function off(types) {
            var type;
            if (!this._callbacks) {
                return this;
            }
            types = types.split(splitter);
            while (type = types.shift()) {
                delete this._callbacks[type];
            }
            return this;
        }
        function trigger(types) {
            var type, callbacks, args, syncFlush, asyncFlush;
            if (!this._callbacks) {
                return this;
            }
            types = types.split(splitter);
            args = [].slice.call(arguments, 1);
            while ((type = types.shift()) && (callbacks = this._callbacks[type])) {
                syncFlush = getFlush(callbacks.sync, this, [type].concat(args));
                asyncFlush = getFlush(callbacks.async, this, [type].concat(args));
                syncFlush() && nextTick(asyncFlush);
            }
            return this;
        }
        function getFlush(callbacks, context, args) {
            return flush;
            function flush() {
                var cancelled;
                for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) {
                    cancelled = callbacks[i].apply(context, args) === false;
                }
                return !cancelled;
            }
        }
        function getNextTick() {
            var nextTickFn;
            if (window.setImmediate) {
                nextTickFn = function nextTickSetImmediate(fn) {
                    setImmediate(function () {
                        fn();
                    });
                };
            } else {
                nextTickFn = function nextTickSetTimeout(fn) {
                    setTimeout(function () {
                        fn();
                    }, 0);
                };
            }
            return nextTickFn;
        }
        function bindContext(fn, context) {
            return fn.bind ? fn.bind(context) : function () {
                fn.apply(context, [].slice.call(arguments, 0));
            };
        }
    }();
    var highlight = function (doc) {
        "use strict";
        var defaults = {
            node: null,
            pattern: null,
            tagName: "strong",
            className: null,
            wordsOnly: false,
            caseSensitive: false
        };
        return function hightlight(o) {
            var regex;
            o = _.mixin({}, defaults, o);
            if (!o.node || !o.pattern) {
                return;
            }
            o.pattern = _.isArray(o.pattern) ? o.pattern : [o.pattern];
            regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly);
            traverse(o.node, hightlightTextNode);
            function hightlightTextNode(textNode) {
                var match, patternNode, wrapperNode;
                if (match = regex.exec(textNode.data)) {
                    wrapperNode = doc.createElement(o.tagName);
                    o.className && (wrapperNode.className = o.className);
                    patternNode = textNode.splitText(match.index);
                    patternNode.splitText(match[0].length);
                    wrapperNode.appendChild(patternNode.cloneNode(true));
                    textNode.parentNode.replaceChild(wrapperNode, patternNode);
                }
                return !!match;
            }
            function traverse(el, hightlightTextNode) {
                var childNode, TEXT_NODE_TYPE = 3;
                for (var i = 0; i < el.childNodes.length; i++) {
                    childNode = el.childNodes[i];
                    if (childNode.nodeType === TEXT_NODE_TYPE) {
                        i += hightlightTextNode(childNode) ? 1 : 0;
                    } else {
                        traverse(childNode, hightlightTextNode);
                    }
                }
            }
        };
        function getRegex(patterns, caseSensitive, wordsOnly) {
            var escapedPatterns = [], regexStr;
            for (var i = 0, len = patterns.length; i < len; i++) {
                escapedPatterns.push(_.escapeRegExChars(patterns[i]));
            }
            regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")";
            return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i");
        }
    }(window.document);
    var Input = function () {
        "use strict";
        var specialKeyCodeMap;
        specialKeyCodeMap = {
            9: "tab",
            27: "esc",
            37: "left",
            39: "right",
            13: "enter",
            38: "up",
            40: "down"
        };
        function Input(o, www) {
            o = o || {};
            if (!o.input) {
                $.error("input is missing");
            }
            www.mixin(this);
            this.$hint = $(o.hint);
            this.$input = $(o.input);
            this.query = this.$input.val();
            this.queryWhenFocused = this.hasFocus() ? this.query : null;
            this.$overflowHelper = buildOverflowHelper(this.$input);
            this._checkLanguageDirection();
            if (this.$hint.length === 0) {
                this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop;
            }
        }
        Input.normalizeQuery = function (str) {
            return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " ");
        };
        _.mixin(Input.prototype, EventEmitter, {
            _onBlur: function onBlur() {
                this.resetInputValue();
                this.trigger("blurred");
            },
            _onFocus: function onFocus() {
                this.queryWhenFocused = this.query;
                this.trigger("focused");
            },
            _onKeydown: function onKeydown($e) {
                var keyName = specialKeyCodeMap[$e.which || $e.keyCode];
                this._managePreventDefault(keyName, $e);
                if (keyName && this._shouldTrigger(keyName, $e)) {
                    this.trigger(keyName + "Keyed", $e);
                }
            },
            _onInput: function onInput() {
                this._setQuery(this.getInputValue());
                this.clearHintIfInvalid();
                this._checkLanguageDirection();
            },
            _managePreventDefault: function managePreventDefault(keyName, $e) {
                var preventDefault;
                switch (keyName) {
                    case "up":
                    case "down":
                        preventDefault = !withModifier($e);
                        break;

                    default:
                        preventDefault = false;
                }
                preventDefault && $e.preventDefault();
            },
            _shouldTrigger: function shouldTrigger(keyName, $e) {
                var trigger;
                switch (keyName) {
                    case "tab":
                        trigger = !withModifier($e);
                        break;

                    default:
                        trigger = true;
                }
                return trigger;
            },
            _checkLanguageDirection: function checkLanguageDirection() {
                var dir = (this.$input.css("direction") || "ltr").toLowerCase();
                if (this.dir !== dir) {
                    this.dir = dir;
                    this.$hint.attr("dir", dir);
                    this.trigger("langDirChanged", dir);
                }
            },
            _setQuery: function setQuery(val, silent) {
                var areEquivalent, hasDifferentWhitespace;
                areEquivalent = areQueriesEquivalent(val, this.query);
                hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false;
                this.query = val;
                if (!silent && !areEquivalent) {
                    this.trigger("queryChanged", this.query);
                } else if (!silent && hasDifferentWhitespace) {
                    this.trigger("whitespaceChanged", this.query);
                }
            },
            bind: function () {
                var that = this, onBlur, onFocus, onKeydown, onInput;
                onBlur = _.bind(this._onBlur, this);
                onFocus = _.bind(this._onFocus, this);
                onKeydown = _.bind(this._onKeydown, this);
                onInput = _.bind(this._onInput, this);
                this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown);
                if (!_.isMsie() || _.isMsie() > 9) {
                    this.$input.on("input.tt", onInput);
                } else {
                    this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function ($e) {
                        if (specialKeyCodeMap[$e.which || $e.keyCode]) {
                            return;
                        }
                        _.defer(_.bind(that._onInput, that, $e));
                    });
                }
                return this;
            },
            focus: function focus() {
                this.$input.focus();
            },
            blur: function blur() {
                this.$input.blur();
            },
            getLangDir: function getLangDir() {
                return this.dir;
            },
            getQuery: function getQuery() {
                return this.query || "";
            },
            setQuery: function setQuery(val, silent) {
                this.setInputValue(val);
                this._setQuery(val, silent);
            },
            hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() {
                return this.query !== this.queryWhenFocused;
            },
            getInputValue: function getInputValue() {
                return this.$input.val();
            },
            setInputValue: function setInputValue(value) {
                this.$input.val(value);
                this.clearHintIfInvalid();
                this._checkLanguageDirection();
            },
            resetInputValue: function resetInputValue() {
                this.setInputValue(this.query);
            },
            getHint: function getHint() {
                return this.$hint.val();
            },
            setHint: function setHint(value) {
                this.$hint.val(value);
            },
            clearHint: function clearHint() {
                this.setHint("");
            },
            clearHintIfInvalid: function clearHintIfInvalid() {
                var val, hint, valIsPrefixOfHint, isValid;
                val = this.getInputValue();
                hint = this.getHint();
                valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0;
                isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow();
                !isValid && this.clearHint();
            },
            hasFocus: function hasFocus() {
                return this.$input.is(":focus");
            },
            hasOverflow: function hasOverflow() {
                var constraint = this.$input.width() - 2;
                this.$overflowHelper.text(this.getInputValue());
                return this.$overflowHelper.width() >= constraint;
            },
            isCursorAtEnd: function () {
                var valueLength, selectionStart, range;
                valueLength = this.$input.val().length;
                selectionStart = this.$input[0].selectionStart;
                if (_.isNumber(selectionStart)) {
                    return selectionStart === valueLength;
                } else if (document.selection) {
                    range = document.selection.createRange();
                    range.moveStart("character", -valueLength);
                    return valueLength === range.text.length;
                }
                return true;
            },
            destroy: function destroy() {
                this.$hint.off(".tt");
                this.$input.off(".tt");
                this.$overflowHelper.remove();
                this.$hint = this.$input = this.$overflowHelper = $("<div>");
            }
        });
        return Input;
        function buildOverflowHelper($input) {
            return $('<pre aria-hidden="true"></pre>').css({
                position: "absolute",
                visibility: "hidden",
                whiteSpace: "pre",
                fontFamily: $input.css("font-family"),
                fontSize: $input.css("font-size"),
                fontStyle: $input.css("font-style"),
                fontVariant: $input.css("font-variant"),
                fontWeight: $input.css("font-weight"),
                wordSpacing: $input.css("word-spacing"),
                letterSpacing: $input.css("letter-spacing"),
                textIndent: $input.css("text-indent"),
                textRendering: $input.css("text-rendering"),
                textTransform: $input.css("text-transform")
            }).insertAfter($input);
        }
        function areQueriesEquivalent(a, b) {
            return Input.normalizeQuery(a) === Input.normalizeQuery(b);
        }
        function withModifier($e) {
            return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey;
        }
    }();
    var Dataset = function () {
        "use strict";
        var keys, nameGenerator;
        keys = {
            val: "tt-selectable-display",
            obj: "tt-selectable-object"
        };
        nameGenerator = _.getIdGenerator();
        function Dataset(o, www) {
            o = o || {};
            o.templates = o.templates || {};
            o.templates.notFound = o.templates.notFound || o.templates.empty;
            if (!o.source) {
                $.error("missing source");
            }
            if (!o.node) {
                $.error("missing node");
            }
            if (o.name && !isValidName(o.name)) {
                $.error("invalid dataset name: " + o.name);
            }
            www.mixin(this);
            this.highlight = !!o.highlight;
            this.name = o.name || nameGenerator();
            this.limit = o.limit || 5;
            this.displayFn = getDisplayFn(o.display || o.displayKey);
            this.templates = getTemplates(o.templates, this.displayFn);
            this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source;
            this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async;
            this._resetLastSuggestion();
            this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name);
        }
        Dataset.extractData = function extractData(el) {
            var $el = $(el);
            if ($el.data(keys.obj)) {
                return {
                    val: $el.data(keys.val) || "",
                    obj: $el.data(keys.obj) || null
                };
            }
            return null;
        };
        _.mixin(Dataset.prototype, EventEmitter, {
            _overwrite: function overwrite(query, suggestions) {
                suggestions = suggestions || [];
                if (suggestions.length) {
                    this._renderSuggestions(query, suggestions);
                } else if (this.async && this.templates.pending) {
                    this._renderPending(query);
                } else if (!this.async && this.templates.notFound) {
                    this._renderNotFound(query);
                } else {
                    this._empty();
                }
                this.trigger("rendered", this.name, suggestions, false);
            },
            _append: function append(query, suggestions) {
                suggestions = suggestions || [];
                if (suggestions.length && this.$lastSuggestion.length) {
                    this._appendSuggestions(query, suggestions);
                } else if (suggestions.length) {
                    this._renderSuggestions(query, suggestions);
                } else if (!this.$lastSuggestion.length && this.templates.notFound) {
                    this._renderNotFound(query);
                }
                this.trigger("rendered", this.name, suggestions, true);
            },
            _renderSuggestions: function renderSuggestions(query, suggestions) {
                var $fragment;
                $fragment = this._getSuggestionsFragment(query, suggestions);
                this.$lastSuggestion = $fragment.children().last();
                this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions));
            },
            _appendSuggestions: function appendSuggestions(query, suggestions) {
                var $fragment, $lastSuggestion;
                $fragment = this._getSuggestionsFragment(query, suggestions);
                $lastSuggestion = $fragment.children().last();
                this.$lastSuggestion.after($fragment);
                this.$lastSuggestion = $lastSuggestion;
            },
            _renderPending: function renderPending(query) {
                var template = this.templates.pending;
                this._resetLastSuggestion();
                template && this.$el.html(template({
                    query: query,
                    dataset: this.name
                }));
            },
            _renderNotFound: function renderNotFound(query) {
                var template = this.templates.notFound;
                this._resetLastSuggestion();
                template && this.$el.html(template({
                    query: query,
                    dataset: this.name
                }));
            },
            _empty: function empty() {
                this.$el.empty();
                this._resetLastSuggestion();
            },
            _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) {
                var that = this, fragment;
                fragment = document.createDocumentFragment();
                _.each(suggestions, function getSuggestionNode(suggestion) {
                    var $el, context;
                    context = that._injectQuery(query, suggestion);
                    $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable);
                    fragment.appendChild($el[0]);
                });
                this.highlight && highlight({
                    className: this.classes.highlight,
                    node: fragment,
                    pattern: query
                });
                return $(fragment);
            },
            _getFooter: function getFooter(query, suggestions) {
                return this.templates.footer ? this.templates.footer({
                    query: query,
                    suggestions: suggestions,
                    dataset: this.name
                }) : null;
            },
            _getHeader: function getHeader(query, suggestions) {
                return this.templates.header ? this.templates.header({
                    query: query,
                    suggestions: suggestions,
                    dataset: this.name
                }) : null;
            },
            _resetLastSuggestion: function resetLastSuggestion() {
                this.$lastSuggestion = $();
            },
            _injectQuery: function injectQuery(query, obj) {
                return _.isObject(obj) ? _.mixin({
                    _query: query
                }, obj) : obj;
            },
            update: function update(query) {
                var that = this, canceled = false, syncCalled = false, rendered = 0;
                this.cancel();
                this.cancel = function cancel() {
                    canceled = true;
                    that.cancel = $.noop;
                    that.async && that.trigger("asyncCanceled", query);
                };
                this.source(query, sync, async);
                !syncCalled && sync([]);
                function sync(suggestions) {
                    if (syncCalled) {
                        return;
                    }
                    syncCalled = true;
                    suggestions = (suggestions || []).slice(0, that.limit);
                    rendered = suggestions.length;
                    that._overwrite(query, suggestions);
                    if (rendered < that.limit && that.async) {
                        that.trigger("asyncRequested", query);
                    }
                }
                function async(suggestions) {
                    suggestions = suggestions || [];
                    if (!canceled && rendered < that.limit) {
                        that.cancel = $.noop;
                        rendered += suggestions.length;
                        that._append(query, suggestions.slice(0, that.limit - rendered));
                        that.async && that.trigger("asyncReceived", query);
                    }
                }
            },
            cancel: $.noop,
            clear: function clear() {
                this._empty();
                this.cancel();
                this.trigger("cleared");
            },
            isEmpty: function isEmpty() {
                return this.$el.is(":empty");
            },
            destroy: function destroy() {
                this.$el = $("<div>");
            }
        });
        return Dataset;
        function getDisplayFn(display) {
            display = display || _.stringify;
            return _.isFunction(display) ? display : displayFn;
            function displayFn(obj) {
                return obj[display];
            }
        }
        function getTemplates(templates, displayFn) {
            return {
                notFound: templates.notFound && _.templatify(templates.notFound),
                pending: templates.pending && _.templatify(templates.pending),
                header: templates.header && _.templatify(templates.header),
                footer: templates.footer && _.templatify(templates.footer),
                suggestion: templates.suggestion || suggestionTemplate
            };
            function suggestionTemplate(context) {
                return $("<div>").text(displayFn(context));
            }
        }
        function isValidName(str) {
            return /^[_a-zA-Z0-9-]+$/.test(str);
        }
    }();
    var Menu = function () {
        "use strict";
        function Menu(o, www) {
            var that = this;
            o = o || {};
            if (!o.node) {
                $.error("node is required");
            }
            www.mixin(this);
            this.$node = $(o.node);
            this.query = null;
            this.datasets = _.map(o.datasets, initializeDataset);
            function initializeDataset(oDataset) {
                var node = that.$node.find(oDataset.node).first();
                oDataset.node = node.length ? node : $("<div>").appendTo(that.$node);
                return new Dataset(oDataset, www);
            }
        }
        _.mixin(Menu.prototype, EventEmitter, {
            _onSelectableClick: function onSelectableClick($e) {
                this.trigger("selectableClicked", $($e.currentTarget));
            },
            _onRendered: function onRendered(type, dataset, suggestions, async) {
                this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
                this.trigger("datasetRendered", dataset, suggestions, async);
            },
            _onCleared: function onCleared() {
                this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty());
                this.trigger("datasetCleared");
            },
            _propagate: function propagate() {
                this.trigger.apply(this, arguments);
            },
            _allDatasetsEmpty: function allDatasetsEmpty() {
                return _.every(this.datasets, isDatasetEmpty);
                function isDatasetEmpty(dataset) {
                    return dataset.isEmpty();
                }
            },
            _getSelectables: function getSelectables() {
                return this.$node.find(this.selectors.selectable);
            },
            _removeCursor: function _removeCursor() {
                var $selectable = this.getActiveSelectable();
                $selectable && $selectable.removeClass(this.classes.cursor);
            },
            _ensureVisible: function ensureVisible($el) {
                var elTop, elBottom, nodeScrollTop, nodeHeight;
                elTop = $el.position().top;
                elBottom = elTop + $el.outerHeight(true);
                nodeScrollTop = this.$node.scrollTop();
                nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10);
                if (elTop < 0) {
                    this.$node.scrollTop(nodeScrollTop + elTop);
                } else if (nodeHeight < elBottom) {
                    this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight));
                }
            },
            bind: function () {
                var that = this, onSelectableClick;
                onSelectableClick = _.bind(this._onSelectableClick, this);
                this.$node.on("click.tt", this.selectors.selectable, onSelectableClick);
                _.each(this.datasets, function (dataset) {
                    dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that);
                });
                return this;
            },
            isOpen: function isOpen() {
                return this.$node.hasClass(this.classes.open);
            },
            open: function open() {
                this.$node.addClass(this.classes.open);
            },
            close: function close() {
                this.$node.removeClass(this.classes.open);
                this._removeCursor();
            },
            setLanguageDirection: function setLanguageDirection(dir) {
                this.$node.attr("dir", dir);
            },
            selectableRelativeToCursor: function selectableRelativeToCursor(delta) {
                var $selectables, $oldCursor, oldIndex, newIndex;
                $oldCursor = this.getActiveSelectable();
                $selectables = this._getSelectables();
                oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1;
                newIndex = oldIndex + delta;
                newIndex = (newIndex + 1) % ($selectables.length + 1) - 1;
                newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex;
                return newIndex === -1 ? null : $selectables.eq(newIndex);
            },
            setCursor: function setCursor($selectable) {
                this._removeCursor();
                if ($selectable = $selectable && $selectable.first()) {
                    $selectable.addClass(this.classes.cursor);
                    this._ensureVisible($selectable);
                }
            },
            getSelectableData: function getSelectableData($el) {
                return $el && $el.length ? Dataset.extractData($el) : null;
            },
            getActiveSelectable: function getActiveSelectable() {
                var $selectable = this._getSelectables().filter(this.selectors.cursor).first();
                return $selectable.length ? $selectable : null;
            },
            getTopSelectable: function getTopSelectable() {
                var $selectable = this._getSelectables().first();
                return $selectable.length ? $selectable : null;
            },
            update: function update(query) {
                var isValidUpdate = query !== this.query;
                if (isValidUpdate) {
                    this.query = query;
                    _.each(this.datasets, updateDataset);
                }
                return isValidUpdate;
                function updateDataset(dataset) {
                    dataset.update(query);
                }
            },
            empty: function empty() {
                _.each(this.datasets, clearDataset);
                this.query = null;
                this.$node.addClass(this.classes.empty);
                function clearDataset(dataset) {
                    dataset.clear();
                }
            },
            destroy: function destroy() {
                this.$node.off(".tt");
                this.$node = $("<div>");
                _.each(this.datasets, destroyDataset);
                function destroyDataset(dataset) {
                    dataset.destroy();
                }
            }
        });
        return Menu;
    }();
    var DefaultMenu = function () {
        "use strict";
        var s = Menu.prototype;
        function DefaultMenu() {
            Menu.apply(this, [].slice.call(arguments, 0));
        }
        _.mixin(DefaultMenu.prototype, Menu.prototype, {
            open: function open() {
                !this._allDatasetsEmpty() && this._show();
                return s.open.apply(this, [].slice.call(arguments, 0));
            },
            close: function close() {
                this._hide();
                return s.close.apply(this, [].slice.call(arguments, 0));
            },
            _onRendered: function onRendered() {
                if (this._allDatasetsEmpty()) {
                    this._hide();
                } else {
                    this.isOpen() && this._show();
                }
                return s._onRendered.apply(this, [].slice.call(arguments, 0));
            },
            _onCleared: function onCleared() {
                if (this._allDatasetsEmpty()) {
                    this._hide();
                } else {
                    this.isOpen() && this._show();
                }
                return s._onCleared.apply(this, [].slice.call(arguments, 0));
            },
            setLanguageDirection: function setLanguageDirection(dir) {
                this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl);
                return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0));
            },
            _hide: function hide() {
                this.$node.hide();
            },
            _show: function show() {
                this.$node.css("display", "block");
            }
        });
        return DefaultMenu;
    }();
    var Typeahead = function () {
        "use strict";
        function Typeahead(o, www) {
            var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged;
            o = o || {};
            if (!o.input) {
                $.error("missing input");
            }
            if (!o.menu) {
                $.error("missing menu");
            }
            if (!o.eventBus) {
                $.error("missing event bus");
            }
            www.mixin(this);
            this.eventBus = o.eventBus;
            this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
            this.input = o.input;
            this.menu = o.menu;
            this.enabled = true;
            this.active = false;
            this.input.hasFocus() && this.activate();
            this.dir = this.input.getLangDir();
            this._hacks();
            this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this);
            onFocused = c(this, "activate", "open", "_onFocused");
            onBlurred = c(this, "deactivate", "_onBlurred");
            onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed");
            onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed");
            onEscKeyed = c(this, "isActive", "_onEscKeyed");
            onUpKeyed = c(this, "isActive", "open", "_onUpKeyed");
            onDownKeyed = c(this, "isActive", "open", "_onDownKeyed");
            onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed");
            onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed");
            onQueryChanged = c(this, "_openIfActive", "_onQueryChanged");
            onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged");
            this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this);
        }
        _.mixin(Typeahead.prototype, {
            _hacks: function hacks() {
                var $input, $menu;
                $input = this.input.$input || $("<div>");
                $menu = this.menu.$node || $("<div>");
                $input.on("blur.tt", function ($e) {
                    var active, isActive, hasActive;
                    active = document.activeElement;
                    isActive = $menu.is(active);
                    hasActive = $menu.has(active).length > 0;
                    if (_.isMsie() && (isActive || hasActive)) {
                        $e.preventDefault();
                        $e.stopImmediatePropagation();
                        _.defer(function () {
                            $input.focus();
                        });
                    }
                });
                $menu.on("mousedown.tt", function ($e) {
                    $e.preventDefault();
                });
            },
            _onSelectableClicked: function onSelectableClicked(type, $el) {
                this.select($el);
            },
            _onDatasetCleared: function onDatasetCleared() {
                this._updateHint();
            },
            _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) {
                this._updateHint();
                this.eventBus.trigger("render", suggestions, async, dataset);
            },
            _onAsyncRequested: function onAsyncRequested(type, dataset, query) {
                this.eventBus.trigger("asyncrequest", query, dataset);
            },
            _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) {
                this.eventBus.trigger("asynccancel", query, dataset);
            },
            _onAsyncReceived: function onAsyncReceived(type, dataset, query) {
                this.eventBus.trigger("asyncreceive", query, dataset);
            },
            _onFocused: function onFocused() {
                this._minLengthMet() && this.menu.update(this.input.getQuery());
            },
            _onBlurred: function onBlurred() {
                if (this.input.hasQueryChangedSinceLastFocus()) {
                    this.eventBus.trigger("change", this.input.getQuery());
                }
            },
            _onEnterKeyed: function onEnterKeyed(type, $e) {
                var $selectable;
                if ($selectable = this.menu.getActiveSelectable()) {
                    this.select($selectable) && $e.preventDefault();
                }
            },
            _onTabKeyed: function onTabKeyed(type, $e) {
                var $selectable;
                if ($selectable = this.menu.getActiveSelectable()) {
                    this.select($selectable) && $e.preventDefault();
                } else if ($selectable = this.menu.getTopSelectable()) {
                    //this.autocomplete($selectable) && $e.preventDefault(); // <-- Comment out
                    this.close() && $e.preventDefault();  // <--- Add this
                }
            },
            _onEscKeyed: function onEscKeyed() {
                this.close();
            },
            _onUpKeyed: function onUpKeyed() {
                this.moveCursor(-1);
            },
            _onDownKeyed: function onDownKeyed() {
                this.moveCursor(+1);
            },
            _onLeftKeyed: function onLeftKeyed() {
                if (this.dir === "rtl" && this.input.isCursorAtEnd()) {
                    this.autocomplete(this.menu.getTopSelectable());
                }
            },
            _onRightKeyed: function onRightKeyed() {
                if (this.dir === "ltr" && this.input.isCursorAtEnd()) {
                    this.autocomplete(this.menu.getTopSelectable());
                }
            },
            _onQueryChanged: function onQueryChanged(e, query) {
                this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty();
            },
            _onWhitespaceChanged: function onWhitespaceChanged() {
                this._updateHint();
            },
            _onLangDirChanged: function onLangDirChanged(e, dir) {
                if (this.dir !== dir) {
                    this.dir = dir;
                    this.menu.setLanguageDirection(dir);
                }
            },
            _openIfActive: function openIfActive() {
                this.isActive() && this.open();
            },
            _minLengthMet: function minLengthMet(query) {
                query = _.isString(query) ? query : this.input.getQuery() || "";
                return query.length >= this.minLength;
            },
            _updateHint: function updateHint() {
                var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match;
                $selectable = this.menu.getTopSelectable();
                data = this.menu.getSelectableData($selectable);
                val = this.input.getInputValue();
                if (data && !_.isBlankString(val) && !this.input.hasOverflow()) {
                    query = Input.normalizeQuery(val);
                    escapedQuery = _.escapeRegExChars(query);
                    frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i");
                    match = frontMatchRegEx.exec(data.val);
                    match && this.input.setHint(val + match[1]);
                } else {
                    this.input.clearHint();
                }
            },
            isEnabled: function isEnabled() {
                return this.enabled;
            },
            enable: function enable() {
                this.enabled = true;
            },
            disable: function disable() {
                this.enabled = false;
            },
            isActive: function isActive() {
                return this.active;
            },
            activate: function activate() {
                if (this.isActive()) {
                    return true;
                } else if (!this.isEnabled() || this.eventBus.before("active")) {
                    return false;
                } else {
                    this.active = true;
                    this.eventBus.trigger("active");
                    return true;
                }
            },
            deactivate: function deactivate() {
                if (!this.isActive()) {
                    return true;
                } else if (this.eventBus.before("idle")) {
                    return false;
                } else {
                    this.active = false;
                    this.close();
                    this.eventBus.trigger("idle");
                    return true;
                }
            },
            isOpen: function isOpen() {
                return this.menu.isOpen();
            },
            open: function open() {
                if (!this.isOpen() && !this.eventBus.before("open")) {
                    this.menu.open();
                    this._updateHint();
                    this.eventBus.trigger("open");
                }
                return this.isOpen();
            },
            close: function close() {
                if (this.isOpen() && !this.eventBus.before("close")) {
                    this.menu.close();
                    this.input.clearHint();
                    this.input.resetInputValue();
                    this.eventBus.trigger("close");
                }
                return !this.isOpen();
            },
            setVal: function setVal(val) {
                this.input.setQuery(_.toStr(val));
            },
            getVal: function getVal() {
                return this.input.getQuery();
            },
            select: function select($selectable) {
                var data = this.menu.getSelectableData($selectable);
                if (data && !this.eventBus.before("select", data.obj)) {
                    this.input.setQuery(data.val, true);
                    this.eventBus.trigger("select", data.obj);
                    this.close();
                    return true;
                }
                return false;
            },
            autocomplete: function autocomplete($selectable) {
                var query, data, isValid;
                query = this.input.getQuery();
                data = this.menu.getSelectableData($selectable);
                isValid = data && query !== data.val;
                if (isValid && !this.eventBus.before("autocomplete", data.obj)) {
                    this.input.setQuery(data.val);
                    this.eventBus.trigger("autocomplete", data.obj);
                    return true;
                }
                return false;
            },
            moveCursor: function moveCursor(delta) {
                var query, $candidate, data, payload, cancelMove;
                query = this.input.getQuery();
                $candidate = this.menu.selectableRelativeToCursor(delta);
                data = this.menu.getSelectableData($candidate);
                payload = data ? data.obj : null;
                cancelMove = this._minLengthMet() && this.menu.update(query);
                if (!cancelMove && !this.eventBus.before("cursorchange", payload)) {
                    this.menu.setCursor($candidate);
                    if (data) {
                        this.input.setInputValue(data.val);
                    } else {
                        this.input.resetInputValue();
                        this._updateHint();
                    }
                    this.eventBus.trigger("cursorchange", payload);
                    return true;
                }
                return false;
            },
            destroy: function destroy() {
                this.input.destroy();
                this.menu.destroy();
            }
        });
        return Typeahead;
        function c(ctx) {
            var methods = [].slice.call(arguments, 1);
            return function () {
                var args = [].slice.call(arguments);
                _.each(methods, function (method) {
                    return ctx[method].apply(ctx, args);
                });
            };
        }
    }();
    (function () {
        "use strict";
        var old, keys, methods;
        old = $.fn.typeahead;
        keys = {
            www: "tt-www",
            attrs: "tt-attrs",
            typeahead: "tt-typeahead"
        };
        methods = {
            initialize: function initialize(o, datasets) {
                var www;
                datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1);
                o = o || {};
                www = WWW(o.classNames);
                return this.each(attach);
                function attach() {
                    var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor;
                    _.each(datasets, function (d) {
                        d.highlight = !!o.highlight;
                    });
                    $input = $(this);
                    $wrapper = $(www.html.wrapper);
                    $hint = $elOrNull(o.hint);
                    $menu = $elOrNull(o.menu);
                    defaultHint = o.hint !== false && !$hint;
                    defaultMenu = o.menu !== false && !$menu;
                    defaultHint && ($hint = buildHintFromInput($input, www));
                    defaultMenu && ($menu = $(www.html.menu).css(www.css.menu));
                    $hint && $hint.val("");
                    $input = prepInput($input, www);
                    if (defaultHint || defaultMenu) {
                        $wrapper.css(www.css.wrapper);
                        $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint);
                        $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null);
                    }
                    MenuConstructor = defaultMenu ? DefaultMenu : Menu;
                    eventBus = new EventBus({
                        el: $input
                    });
                    input = new Input({
                        hint: $hint,
                        input: $input
                    }, www);
                    menu = new MenuConstructor({
                        node: $menu,
                        datasets: datasets
                    }, www);
                    typeahead = new Typeahead({
                        input: input,
                        menu: menu,
                        eventBus: eventBus,
                        minLength: o.minLength
                    }, www);
                    $input.data(keys.www, www);
                    $input.data(keys.typeahead, typeahead);
                }
            },
            isEnabled: function isEnabled() {
                var enabled;
                ttEach(this.first(), function (t) {
                    enabled = t.isEnabled();
                });
                return enabled;
            },
            enable: function enable() {
                ttEach(this, function (t) {
                    t.enable();
                });
                return this;
            },
            disable: function disable() {
                ttEach(this, function (t) {
                    t.disable();
                });
                return this;
            },
            isActive: function isActive() {
                var active;
                ttEach(this.first(), function (t) {
                    active = t.isActive();
                });
                return active;
            },
            activate: function activate() {
                ttEach(this, function (t) {
                    t.activate();
                });
                return this;
            },
            deactivate: function deactivate() {
                ttEach(this, function (t) {
                    t.deactivate();
                });
                return this;
            },
            isOpen: function isOpen() {
                var open;
                ttEach(this.first(), function (t) {
                    open = t.isOpen();
                });
                return open;
            },
            open: function open() {
                ttEach(this, function (t) {
                    t.open();
                });
                return this;
            },
            close: function close() {
                ttEach(this, function (t) {
                    t.close();
                });
                return this;
            },
            select: function select(el) {
                var success = false, $el = $(el);
                ttEach(this.first(), function (t) {
                    success = t.select($el);
                });
                return success;
            },
            autocomplete: function autocomplete(el) {
                var success = false, $el = $(el);
                ttEach(this.first(), function (t) {
                    success = t.autocomplete($el);
                });
                return success;
            },
            moveCursor: function moveCursoe(delta) {
                var success = false;
                ttEach(this.first(), function (t) {
                    success = t.moveCursor(delta);
                });
                return success;
            },
            val: function val(newVal) {
                var query;
                if (!arguments.length) {
                    ttEach(this.first(), function (t) {
                        query = t.getVal();
                    });
                    return query;
                } else {
                    ttEach(this, function (t) {
                        t.setVal(newVal);
                    });
                    return this;
                }
            },
            destroy: function destroy() {
                ttEach(this, function (typeahead, $input) {
                    revert($input);
                    typeahead.destroy();
                });
                return this;
            }
        };
        $.fn.typeahead = function (method) {
            if (methods[method]) {
                return methods[method].apply(this, [].slice.call(arguments, 1));
            } else {
                return methods.initialize.apply(this, arguments);
            }
        };
        $.fn.typeahead.noConflict = function noConflict() {
            $.fn.typeahead = old;
            return this;
        };
        function ttEach($els, fn) {
            $els.each(function () {
                var $input = $(this), typeahead;
                (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input);
            });
        }
        function buildHintFromInput($input, www) {
            return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({
                autocomplete: "off",
                spellcheck: "false",
                tabindex: -1
            });
        }
        function prepInput($input, www) {
            $input.data(keys.attrs, {
                dir: $input.attr("dir"),
                autocomplete: $input.attr("autocomplete"),
                spellcheck: $input.attr("spellcheck"),
                style: $input.attr("style")
            });
            $input.addClass(www.classes.input).attr({
                autocomplete: "off",
                spellcheck: false
            });
            try {
                !$input.attr("dir") && $input.attr("dir", "auto");
            } catch (e) { }
            return $input;
        }
        function getBackgroundStyles($el) {
            return {
                backgroundAttachment: $el.css("background-attachment"),
                backgroundClip: $el.css("background-clip"),
                backgroundColor: $el.css("background-color"),
                backgroundImage: $el.css("background-image"),
                backgroundOrigin: $el.css("background-origin"),
                backgroundPosition: $el.css("background-position"),
                backgroundRepeat: $el.css("background-repeat"),
                backgroundSize: $el.css("background-size")
            };
        }
        function revert($input) {
            var www, $wrapper;
            www = $input.data(keys.www);
            $wrapper = $input.parent().filter(www.selectors.wrapper);
            _.each($input.data(keys.attrs), function (val, key) {
                _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
            });
            $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input);
            if ($wrapper.length) {
                $input.detach().insertAfter($wrapper);
                $wrapper.remove();
            }
        }
        function $elOrNull(obj) {
            var isValid, $el;
            isValid = _.isJQuery(obj) || _.isElement(obj);
            $el = isValid ? $(obj).first() : [];
            return $el.length ? $el : null;
        }
    })();
});;
/* ========================================================================
 * bootstrap-switch - v3.3.2
 * http://www.bootstrap-switch.org
 * ========================================================================
 * Copyright 2012-2013 Mattia Larentis
 *
 * ========================================================================
 * 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(){var t=[].slice;!function(e,i){"use strict";var n;return n=function(){function t(t,i){null==i&&(i={}),this.$element=e(t),this.options=e.extend({},e.fn.bootstrapSwitch.defaults,{state:this.$element.is(":checked"),size:this.$element.data("size"),animate:this.$element.data("animate"),disabled:this.$element.is(":disabled"),readonly:this.$element.is("[readonly]"),indeterminate:this.$element.data("indeterminate"),inverse:this.$element.data("inverse"),radioAllOff:this.$element.data("radio-all-off"),onColor:this.$element.data("on-color"),offColor:this.$element.data("off-color"),onText:this.$element.data("on-text"),offText:this.$element.data("off-text"),labelText:this.$element.data("label-text"),handleWidth:this.$element.data("handle-width"),labelWidth:this.$element.data("label-width"),baseClass:this.$element.data("base-class"),wrapperClass:this.$element.data("wrapper-class")},i),this.prevOptions={},this.$wrapper=e("<div>",{"class":function(t){return function(){var e;return e=[""+t.options.baseClass].concat(t._getClasses(t.options.wrapperClass)),e.push(t.options.state?t.options.baseClass+"-on":t.options.baseClass+"-off"),null!=t.options.size&&e.push(t.options.baseClass+"-"+t.options.size),t.options.disabled&&e.push(t.options.baseClass+"-disabled"),t.options.readonly&&e.push(t.options.baseClass+"-readonly"),t.options.indeterminate&&e.push(t.options.baseClass+"-indeterminate"),t.options.inverse&&e.push(t.options.baseClass+"-inverse"),t.$element.attr("id")&&e.push(t.options.baseClass+"-id-"+t.$element.attr("id")),e.join(" ")}}(this)()}),this.$container=e("<div>",{"class":this.options.baseClass+"-container"}),this.$on=e("<span>",{html:this.options.onText,"class":this.options.baseClass+"-handle-on "+this.options.baseClass+"-"+this.options.onColor}),this.$off=e("<span>",{html:this.options.offText,"class":this.options.baseClass+"-handle-off "+this.options.baseClass+"-"+this.options.offColor}),this.$label=e("<span>",{html:this.options.labelText,"class":this.options.baseClass+"-label"}),this.$element.on("init.bootstrapSwitch",function(e){return function(){return e.options.onInit.apply(t,arguments)}}(this)),this.$element.on("switchChange.bootstrapSwitch",function(i){return function(n){return!1===i.options.onSwitchChange.apply(t,arguments)?i.$element.is(":radio")?e("[name='"+i.$element.attr("name")+"']").trigger("previousState.bootstrapSwitch",!0):i.$element.trigger("previousState.bootstrapSwitch",!0):void 0}}(this)),this.$container=this.$element.wrap(this.$container).parent(),this.$wrapper=this.$container.wrap(this.$wrapper).parent(),this.$element.before(this.options.inverse?this.$off:this.$on).before(this.$label).before(this.options.inverse?this.$on:this.$off),this.options.indeterminate&&this.$element.prop("indeterminate",!0),this._init(),this._elementHandlers(),this._handleHandlers(),this._labelHandlers(),this._formHandler(),this._externalLabelHandler(),this.$element.trigger("init.bootstrapSwitch",this.options.state)}return t.prototype._constructor=t,t.prototype.setPrevOptions=function(){return this.prevOptions=e.extend(!0,{},this.options)},t.prototype.state=function(t,i){return"undefined"==typeof t?this.options.state:this.options.disabled||this.options.readonly?this.$element:this.options.state&&!this.options.radioAllOff&&this.$element.is(":radio")?this.$element:(this.$element.is(":radio")?e("[name='"+this.$element.attr("name")+"']").trigger("setPreviousOptions.bootstrapSwitch"):this.$element.trigger("setPreviousOptions.bootstrapSwitch"),this.options.indeterminate&&this.indeterminate(!1),t=!!t,this.$element.prop("checked",t).trigger("change.bootstrapSwitch",i),this.$element)},t.prototype.toggleState=function(t){return this.options.disabled||this.options.readonly?this.$element:this.options.indeterminate?(this.indeterminate(!1),this.state(!0)):this.$element.prop("checked",!this.options.state).trigger("change.bootstrapSwitch",t)},t.prototype.size=function(t){return"undefined"==typeof t?this.options.size:(null!=this.options.size&&this.$wrapper.removeClass(this.options.baseClass+"-"+this.options.size),t&&this.$wrapper.addClass(this.options.baseClass+"-"+t),this._width(),this._containerPosition(),this.options.size=t,this.$element)},t.prototype.animate=function(t){return"undefined"==typeof t?this.options.animate:(t=!!t,t===this.options.animate?this.$element:this.toggleAnimate())},t.prototype.toggleAnimate=function(){return this.options.animate=!this.options.animate,this.$wrapper.toggleClass(this.options.baseClass+"-animate"),this.$element},t.prototype.disabled=function(t){return"undefined"==typeof t?this.options.disabled:(t=!!t,t===this.options.disabled?this.$element:this.toggleDisabled())},t.prototype.toggleDisabled=function(){return this.options.disabled=!this.options.disabled,this.$element.prop("disabled",this.options.disabled),this.$wrapper.toggleClass(this.options.baseClass+"-disabled"),this.$element},t.prototype.readonly=function(t){return"undefined"==typeof t?this.options.readonly:(t=!!t,t===this.options.readonly?this.$element:this.toggleReadonly())},t.prototype.toggleReadonly=function(){return this.options.readonly=!this.options.readonly,this.$element.prop("readonly",this.options.readonly),this.$wrapper.toggleClass(this.options.baseClass+"-readonly"),this.$element},t.prototype.indeterminate=function(t){return"undefined"==typeof t?this.options.indeterminate:(t=!!t,t===this.options.indeterminate?this.$element:this.toggleIndeterminate())},t.prototype.toggleIndeterminate=function(){return this.options.indeterminate=!this.options.indeterminate,this.$element.prop("indeterminate",this.options.indeterminate),this.$wrapper.toggleClass(this.options.baseClass+"-indeterminate"),this._containerPosition(),this.$element},t.prototype.inverse=function(t){return"undefined"==typeof t?this.options.inverse:(t=!!t,t===this.options.inverse?this.$element:this.toggleInverse())},t.prototype.toggleInverse=function(){var t,e;return this.$wrapper.toggleClass(this.options.baseClass+"-inverse"),e=this.$on.clone(!0),t=this.$off.clone(!0),this.$on.replaceWith(t),this.$off.replaceWith(e),this.$on=t,this.$off=e,this.options.inverse=!this.options.inverse,this.$element},t.prototype.onColor=function(t){var e;return e=this.options.onColor,"undefined"==typeof t?e:(null!=e&&this.$on.removeClass(this.options.baseClass+"-"+e),this.$on.addClass(this.options.baseClass+"-"+t),this.options.onColor=t,this.$element)},t.prototype.offColor=function(t){var e;return e=this.options.offColor,"undefined"==typeof t?e:(null!=e&&this.$off.removeClass(this.options.baseClass+"-"+e),this.$off.addClass(this.options.baseClass+"-"+t),this.options.offColor=t,this.$element)},t.prototype.onText=function(t){return"undefined"==typeof t?this.options.onText:(this.$on.html(t),this._width(),this._containerPosition(),this.options.onText=t,this.$element)},t.prototype.offText=function(t){return"undefined"==typeof t?this.options.offText:(this.$off.html(t),this._width(),this._containerPosition(),this.options.offText=t,this.$element)},t.prototype.labelText=function(t){return"undefined"==typeof t?this.options.labelText:(this.$label.html(t),this._width(),this.options.labelText=t,this.$element)},t.prototype.handleWidth=function(t){return"undefined"==typeof t?this.options.handleWidth:(this.options.handleWidth=t,this._width(),this._containerPosition(),this.$element)},t.prototype.labelWidth=function(t){return"undefined"==typeof t?this.options.labelWidth:(this.options.labelWidth=t,this._width(),this._containerPosition(),this.$element)},t.prototype.baseClass=function(t){return this.options.baseClass},t.prototype.wrapperClass=function(t){return"undefined"==typeof t?this.options.wrapperClass:(t||(t=e.fn.bootstrapSwitch.defaults.wrapperClass),this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(" ")),this.$wrapper.addClass(this._getClasses(t).join(" ")),this.options.wrapperClass=t,this.$element)},t.prototype.radioAllOff=function(t){return"undefined"==typeof t?this.options.radioAllOff:(t=!!t,t===this.options.radioAllOff?this.$element:(this.options.radioAllOff=t,this.$element))},t.prototype.onInit=function(t){return"undefined"==typeof t?this.options.onInit:(t||(t=e.fn.bootstrapSwitch.defaults.onInit),this.options.onInit=t,this.$element)},t.prototype.onSwitchChange=function(t){return"undefined"==typeof t?this.options.onSwitchChange:(t||(t=e.fn.bootstrapSwitch.defaults.onSwitchChange),this.options.onSwitchChange=t,this.$element)},t.prototype.destroy=function(){var t;return t=this.$element.closest("form"),t.length&&t.off("reset.bootstrapSwitch").removeData("bootstrap-switch"),this.$container.children().not(this.$element).remove(),this.$element.unwrap().unwrap().off(".bootstrapSwitch").removeData("bootstrap-switch"),this.$element},t.prototype._width=function(){var t,e;return t=this.$on.add(this.$off),t.add(this.$label).css("width",""),e="auto"===this.options.handleWidth?Math.max(this.$on.width(),this.$off.width()):this.options.handleWidth,t.width(e),this.$label.width(function(t){return function(i,n){return"auto"!==t.options.labelWidth?t.options.labelWidth:e>n?e:n}}(this)),this._handleWidth=this.$on.outerWidth(),this._labelWidth=this.$label.outerWidth(),this.$container.width(2*this._handleWidth+this._labelWidth),this.$wrapper.width(this._handleWidth+this._labelWidth)},t.prototype._containerPosition=function(t,e){return null==t&&(t=this.options.state),this.$container.css("margin-left",function(e){return function(){var i;return i=[0,"-"+e._handleWidth+"px"],e.options.indeterminate?"-"+e._handleWidth/2+"px":t?e.options.inverse?i[1]:i[0]:e.options.inverse?i[0]:i[1]}}(this)),e?setTimeout(function(){return e()},50):void 0},t.prototype._init=function(){var t,e;return t=function(t){return function(){return t.setPrevOptions(),t._width(),t._containerPosition(null,function(){return t.options.animate?t.$wrapper.addClass(t.options.baseClass+"-animate"):void 0})}}(this),this.$wrapper.is(":visible")?t():e=i.setInterval(function(n){return function(){return n.$wrapper.is(":visible")?(t(),i.clearInterval(e)):void 0}}(this),50)},t.prototype._elementHandlers=function(){return this.$element.on({"setPreviousOptions.bootstrapSwitch":function(t){return function(e){return t.setPrevOptions()}}(this),"previousState.bootstrapSwitch":function(t){return function(e){return t.options=t.prevOptions,t.options.indeterminate&&t.$wrapper.addClass(t.options.baseClass+"-indeterminate"),t.$element.prop("checked",t.options.state).trigger("change.bootstrapSwitch",!0)}}(this),"change.bootstrapSwitch":function(t){return function(i,n){var o;return i.preventDefault(),i.stopImmediatePropagation(),o=t.$element.is(":checked"),t._containerPosition(o),o!==t.options.state?(t.options.state=o,t.$wrapper.toggleClass(t.options.baseClass+"-off").toggleClass(t.options.baseClass+"-on"),n?void 0:(t.$element.is(":radio")&&e("[name='"+t.$element.attr("name")+"']").not(t.$element).prop("checked",!1).trigger("change.bootstrapSwitch",!0),t.$element.trigger("switchChange.bootstrapSwitch",[o]))):void 0}}(this),"focus.bootstrapSwitch":function(t){return function(e){return e.preventDefault(),t.$wrapper.addClass(t.options.baseClass+"-focused")}}(this),"blur.bootstrapSwitch":function(t){return function(e){return e.preventDefault(),t.$wrapper.removeClass(t.options.baseClass+"-focused")}}(this),"keydown.bootstrapSwitch":function(t){return function(e){if(e.which&&!t.options.disabled&&!t.options.readonly)switch(e.which){case 37:return e.preventDefault(),e.stopImmediatePropagation(),t.state(!1);case 39:return e.preventDefault(),e.stopImmediatePropagation(),t.state(!0)}}}(this)})},t.prototype._handleHandlers=function(){return this.$on.on("click.bootstrapSwitch",function(t){return function(e){return e.preventDefault(),e.stopPropagation(),t.state(!1),t.$element.trigger("focus.bootstrapSwitch")}}(this)),this.$off.on("click.bootstrapSwitch",function(t){return function(e){return e.preventDefault(),e.stopPropagation(),t.state(!0),t.$element.trigger("focus.bootstrapSwitch")}}(this))},t.prototype._labelHandlers=function(){return this.$label.on({click:function(t){return t.stopPropagation()},"mousedown.bootstrapSwitch touchstart.bootstrapSwitch":function(t){return function(e){return t._dragStart||t.options.disabled||t.options.readonly?void 0:(e.preventDefault(),e.stopPropagation(),t._dragStart=(e.pageX||e.originalEvent.touches[0].pageX)-parseInt(t.$container.css("margin-left"),10),t.options.animate&&t.$wrapper.removeClass(t.options.baseClass+"-animate"),t.$element.trigger("focus.bootstrapSwitch"))}}(this),"mousemove.bootstrapSwitch touchmove.bootstrapSwitch":function(t){return function(e){var i;if(null!=t._dragStart&&(e.preventDefault(),i=(e.pageX||e.originalEvent.touches[0].pageX)-t._dragStart,!(i<-t._handleWidth||i>0)))return t._dragEnd=i,t.$container.css("margin-left",t._dragEnd+"px")}}(this),"mouseup.bootstrapSwitch touchend.bootstrapSwitch":function(t){return function(e){var i;if(t._dragStart)return e.preventDefault(),t.options.animate&&t.$wrapper.addClass(t.options.baseClass+"-animate"),t._dragEnd?(i=t._dragEnd>-(t._handleWidth/2),t._dragEnd=!1,t.state(t.options.inverse?!i:i)):t.state(!t.options.state),t._dragStart=!1}}(this),"mouseleave.bootstrapSwitch":function(t){return function(e){return t.$label.trigger("mouseup.bootstrapSwitch")}}(this)})},t.prototype._externalLabelHandler=function(){var t;return t=this.$element.closest("label"),t.on("click",function(e){return function(i){return i.preventDefault(),i.stopImmediatePropagation(),i.target===t[0]?e.toggleState():void 0}}(this))},t.prototype._formHandler=function(){var t;return t=this.$element.closest("form"),t.data("bootstrap-switch")?void 0:t.on("reset.bootstrapSwitch",function(){return i.setTimeout(function(){return t.find("input").filter(function(){return e(this).data("bootstrap-switch")}).each(function(){return e(this).bootstrapSwitch("state",this.checked)})},1)}).data("bootstrap-switch",!0)},t.prototype._getClasses=function(t){var i,n,o,s;if(!e.isArray(t))return[this.options.baseClass+"-"+t];for(n=[],o=0,s=t.length;s>o;o++)i=t[o],n.push(this.options.baseClass+"-"+i);return n},t}(),e.fn.bootstrapSwitch=function(){var i,o,s;return o=arguments[0],i=2<=arguments.length?t.call(arguments,1):[],s=this,this.each(function(){var t,a;return t=e(this),a=t.data("bootstrap-switch"),a||t.data("bootstrap-switch",a=new n(this,o)),"string"==typeof o?s=a[o].apply(a,i):void 0}),s},e.fn.bootstrapSwitch.Constructor=n,e.fn.bootstrapSwitch.defaults={state:!0,size:null,animate:!0,disabled:!1,readonly:!1,indeterminate:!1,inverse:!1,radioAllOff:!1,onColor:"primary",offColor:"default",onText:"ON",offText:"OFF",labelText:"&nbsp;",handleWidth:"auto",labelWidth:"auto",baseClass:"bootstrap-switch",wrapperClass:"wrapper",onInit:function(){},onSwitchChange:function(){}}}(window.jQuery,window)}).call(this);;
/* ========================================================================
 * bootstrap-tour - v0.10.3
 * http://bootstraptour.com
 * ========================================================================
 * Copyright 2012-2015 Ulrich Sossou
 *
 * ========================================================================
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://opensource.org/licenses/MIT
 *
 * 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(t){"use strict";function e(){var t=document.createElement("bootstrap"),e={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var o in e)if(void 0!==t.style[o])return{end:e[o]};return!1}t.fn.emulateTransitionEnd=function(e){var o=!1,n=this;t(this).one("bsTransitionEnd",function(){o=!0});var i=function(){o||t(n).trigger(t.support.transition.end)};return setTimeout(i,e),this},t(function(){t.support.transition=e(),t.support.transition&&(t.event.special.bsTransitionEnd={bindType:t.support.transition.end,delegateType:t.support.transition.end,handle:function(e){return t(e.target).is(this)?e.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var n=t(this),i=n.data("bs.tooltip"),r="object"==typeof e&&e;(i||!/destroy|hide/.test(e))&&(i||n.data("bs.tooltip",i=new o(this,r)),"string"==typeof e&&i[e]())})}var o=function(t,e){this.type=null,this.options=null,this.enabled=null,this.timeout=null,this.hoverState=null,this.$element=null,this.inState=null,this.init("tooltip",t,e)};o.VERSION="3.3.7",o.TRANSITION_DURATION=150,o.DEFAULTS={animation:!0,placement:"top",selector:!1,template:'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},o.prototype.init=function(e,o,n){if(this.enabled=!0,this.type=e,this.$element=t(o),this.options=this.getOptions(n),this.$viewport=this.options.viewport&&t(t.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var i=this.options.trigger.split(" "),r=i.length;r--;){var s=i[r];if("click"==s)this.$element.on("click."+this.type,this.options.selector,t.proxy(this.toggle,this));else if("manual"!=s){var a="hover"==s?"mouseenter":"focusin",p="hover"==s?"mouseleave":"focusout";this.$element.on(a+"."+this.type,this.options.selector,t.proxy(this.enter,this)),this.$element.on(p+"."+this.type,this.options.selector,t.proxy(this.leave,this))}}this.options.selector?this._options=t.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},o.prototype.getDefaults=function(){return o.DEFAULTS},o.prototype.getOptions=function(e){return e=t.extend({},this.getDefaults(),this.$element.data(),e),e.delay&&"number"==typeof e.delay&&(e.delay={show:e.delay,hide:e.delay}),e},o.prototype.getDelegateOptions=function(){var e={},o=this.getDefaults();return this._options&&t.each(this._options,function(t,n){o[t]!=n&&(e[t]=n)}),e},o.prototype.enter=function(e){var o=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);return o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o)),e instanceof t.Event&&(o.inState["focusin"==e.type?"focus":"hover"]=!0),o.tip().hasClass("in")||"in"==o.hoverState?(o.hoverState="in",void 0):(clearTimeout(o.timeout),o.hoverState="in",o.options.delay&&o.options.delay.show?(o.timeout=setTimeout(function(){"in"==o.hoverState&&o.show()},o.options.delay.show),void 0):o.show())},o.prototype.isInStateTrue=function(){for(var t in this.inState)if(this.inState[t])return!0;return!1},o.prototype.leave=function(e){var o=e instanceof this.constructor?e:t(e.currentTarget).data("bs."+this.type);return o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o)),e instanceof t.Event&&(o.inState["focusout"==e.type?"focus":"hover"]=!1),o.isInStateTrue()?void 0:(clearTimeout(o.timeout),o.hoverState="out",o.options.delay&&o.options.delay.hide?(o.timeout=setTimeout(function(){"out"==o.hoverState&&o.hide()},o.options.delay.hide),void 0):o.hide())},o.prototype.show=function(){var e=t.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(e);var n=t.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(e.isDefaultPrevented()||!n)return;var i=this,r=this.tip(),s=this.getUID(this.type);this.setContent(),r.attr("id",s),this.$element.attr("aria-describedby",s),this.options.animation&&r.addClass("fade");var a="function"==typeof this.options.placement?this.options.placement.call(this,r[0],this.$element[0]):this.options.placement,p=/\s?auto?\s?/i,h=p.test(a);h&&(a=a.replace(p,"")||"top"),r.detach().css({top:0,left:0,display:"block"}).addClass(a).data("bs."+this.type,this),this.options.container?r.appendTo(this.options.container):r.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var l=this.getPosition(),u=r[0].offsetWidth,c=r[0].offsetHeight;if(h){var d=a,f=this.getPosition(this.$viewport);a="bottom"==a&&l.bottom+c>f.bottom?"top":"top"==a&&l.top-c<f.top?"bottom":"right"==a&&l.right+u>f.width?"left":"left"==a&&l.left-u<f.left?"right":a,r.removeClass(d).addClass(a)}var m=this.getCalculatedOffset(a,l,u,c);this.applyPlacement(m,a);var _=function(){var t=i.hoverState;i.$element.trigger("shown.bs."+i.type),i.hoverState=null,"out"==t&&i.leave(i)};t.support.transition&&this.$tip.hasClass("fade")?r.one("bsTransitionEnd",_).emulateTransitionEnd(o.TRANSITION_DURATION):_()}},o.prototype.applyPlacement=function(e,o){var n=this.tip(),i=n[0].offsetWidth,r=n[0].offsetHeight,s=parseInt(n.css("margin-top"),10),a=parseInt(n.css("margin-left"),10);isNaN(s)&&(s=0),isNaN(a)&&(a=0),e.top+=s,e.left+=a,t.offset.setOffset(n[0],t.extend({using:function(t){n.css({top:Math.round(t.top),left:Math.round(t.left)})}},e),0),n.addClass("in");var p=n[0].offsetWidth,h=n[0].offsetHeight;"top"==o&&h!=r&&(e.top=e.top+r-h);var l=this.getViewportAdjustedDelta(o,e,p,h);l.left?e.left+=l.left:e.top+=l.top;var u=/top|bottom/.test(o),c=u?2*l.left-i+p:2*l.top-r+h,d=u?"offsetWidth":"offsetHeight";n.offset(e),this.replaceArrow(c,n[0][d],u)},o.prototype.replaceArrow=function(t,e,o){this.arrow().css(o?"left":"top",50*(1-t/e)+"%").css(o?"top":"left","")},o.prototype.setContent=function(){var t=this.tip(),e=this.getTitle();t.find(".tooltip-inner")[this.options.html?"html":"text"](e),t.removeClass("fade in top bottom left right")},o.prototype.hide=function(e){function n(){"in"!=i.hoverState&&r.detach(),i.$element&&i.$element.removeAttr("aria-describedby").trigger("hidden.bs."+i.type),e&&e()}var i=this,r=t(this.$tip),s=t.Event("hide.bs."+this.type);return this.$element.trigger(s),s.isDefaultPrevented()?void 0:(r.removeClass("in"),t.support.transition&&r.hasClass("fade")?r.one("bsTransitionEnd",n).emulateTransitionEnd(o.TRANSITION_DURATION):n(),this.hoverState=null,this)},o.prototype.fixTitle=function(){var t=this.$element;(t.attr("title")||"string"!=typeof t.attr("data-original-title"))&&t.attr("data-original-title",t.attr("title")||"").attr("title","")},o.prototype.hasContent=function(){return this.getTitle()},o.prototype.getPosition=function(e){e=e||this.$element;var o=e[0],n="BODY"==o.tagName,i=o.getBoundingClientRect();null==i.width&&(i=t.extend({},i,{width:i.right-i.left,height:i.bottom-i.top}));var r=window.SVGElement&&o instanceof window.SVGElement,s=n?{top:0,left:0}:r?null:e.offset(),a={scroll:n?document.documentElement.scrollTop||document.body.scrollTop:e.scrollTop()},p=n?{width:t(window).width(),height:t(window).height()}:null;return t.extend({},i,a,p,s)},o.prototype.getCalculatedOffset=function(t,e,o,n){return"bottom"==t?{top:e.top+e.height,left:e.left+e.width/2-o/2}:"top"==t?{top:e.top-n,left:e.left+e.width/2-o/2}:"left"==t?{top:e.top+e.height/2-n/2,left:e.left-o}:{top:e.top+e.height/2-n/2,left:e.left+e.width}},o.prototype.getViewportAdjustedDelta=function(t,e,o,n){var i={top:0,left:0};if(!this.$viewport)return i;var r=this.options.viewport&&this.options.viewport.padding||0,s=this.getPosition(this.$viewport);if(/right|left/.test(t)){var a=e.top-r-s.scroll,p=e.top+r-s.scroll+n;a<s.top?i.top=s.top-a:p>s.top+s.height&&(i.top=s.top+s.height-p)}else{var h=e.left-r,l=e.left+r+o;h<s.left?i.left=s.left-h:l>s.right&&(i.left=s.left+s.width-l)}return i},o.prototype.getTitle=function(){var t,e=this.$element,o=this.options;return t=e.attr("data-original-title")||("function"==typeof o.title?o.title.call(e[0]):o.title)},o.prototype.getUID=function(t){do t+=~~(1e6*Math.random());while(document.getElementById(t));return t},o.prototype.tip=function(){if(!this.$tip&&(this.$tip=t(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},o.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},o.prototype.enable=function(){this.enabled=!0},o.prototype.disable=function(){this.enabled=!1},o.prototype.toggleEnabled=function(){this.enabled=!this.enabled},o.prototype.toggle=function(e){var o=this;e&&(o=t(e.currentTarget).data("bs."+this.type),o||(o=new this.constructor(e.currentTarget,this.getDelegateOptions()),t(e.currentTarget).data("bs."+this.type,o))),e?(o.inState.click=!o.inState.click,o.isInStateTrue()?o.enter(o):o.leave(o)):o.tip().hasClass("in")?o.leave(o):o.enter(o)},o.prototype.destroy=function(){var t=this;clearTimeout(this.timeout),this.hide(function(){t.$element.off("."+t.type).removeData("bs."+t.type),t.$tip&&t.$tip.detach(),t.$tip=null,t.$arrow=null,t.$viewport=null,t.$element=null})};var n=t.fn.tooltip;t.fn.tooltip=e,t.fn.tooltip.Constructor=o,t.fn.tooltip.noConflict=function(){return t.fn.tooltip=n,this}}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var n=t(this),i=n.data("bs.popover"),r="object"==typeof e&&e;(i||!/destroy|hide/.test(e))&&(i||n.data("bs.popover",i=new o(this,r)),"string"==typeof e&&i[e]())})}var o=function(t,e){this.init("popover",t,e)};if(!t.fn.tooltip)throw new Error("Popover requires tooltip.js");o.VERSION="3.3.7",o.DEFAULTS=t.extend({},t.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:'<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'}),o.prototype=t.extend({},t.fn.tooltip.Constructor.prototype),o.prototype.constructor=o,o.prototype.getDefaults=function(){return o.DEFAULTS},o.prototype.setContent=function(){var t=this.tip(),e=this.getTitle(),o=this.getContent();t.find(".popover-title")[this.options.html?"html":"text"](e),t.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof o?"html":"append":"text"](o),t.removeClass("fade top bottom left right in"),t.find(".popover-title").html()||t.find(".popover-title").hide()},o.prototype.hasContent=function(){return this.getTitle()||this.getContent()},o.prototype.getContent=function(){var t=this.$element,e=this.options;return t.attr("data-content")||("function"==typeof e.content?e.content.call(t[0]):e.content)},o.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var n=t.fn.popover;t.fn.popover=e,t.fn.popover.Constructor=o,t.fn.popover.noConflict=function(){return t.fn.popover=n,this}}(jQuery),function(t,e){return"function"==typeof define&&define.amd?define(["jquery"],function(o){return t.Tour=e(o)}):"object"==typeof exports?module.exports=e(require("jQuery")):t.Tour=e(t.jQuery)}(window,function(t){var e,o;return o=window.document,e=function(){function e(e){var o;try{o=window.localStorage}catch(n){o=!1}this._options=t.extend({name:"tour",steps:[],container:"body",autoscroll:!0,keyboard:!0,storage:o,debug:!1,backdrop:!1,backdropContainer:"body",backdropPadding:0,redirect:!0,orphan:!1,duration:!1,delay:!1,basePath:"",template:'<div class="popover" role="tooltip"> <div class="arrow"></div> <h3 class="popover-title"></h3> <div class="popover-content"></div> <div class="popover-navigation"> <div class="btn-group"> <button class="btn btn-sm btn-default" data-role="prev">&laquo; Prev</button> <button class="btn btn-sm btn-default" data-role="next">Next &raquo;</button> <button class="btn btn-sm btn-default" data-role="pause-resume" data-pause-text="Pause" data-resume-text="Resume">Pause</button> </div> <button class="btn btn-sm btn-default" data-role="end">End tour</button> </div> </div>',afterSetState:function(){},afterGetState:function(){},afterRemoveState:function(){},onStart:function(){},onEnd:function(){},onShow:function(){},onShown:function(){},onHide:function(){},onHidden:function(){},onNext:function(){},onPrev:function(){},onPause:function(){},onResume:function(){},onRedirectError:function(){}},e),this._force=!1,this._inited=!1,this._current=null,this.backdrop={overlay:null,$element:null,$background:null,backgroundShown:!1,overlayElementShown:!1}}return e.prototype.addSteps=function(t){var e,o,n;for(o=0,n=t.length;n>o;o++)e=t[o],this.addStep(e);return this},e.prototype.addStep=function(t){return this._options.steps.push(t),this},e.prototype.getStep=function(e){return null!=this._options.steps[e]?t.extend({id:"step-"+e,path:"",host:"",placement:"right",title:"",content:"<p></p>",next:e===this._options.steps.length-1?-1:e+1,prev:e-1,animation:!0,container:this._options.container,autoscroll:this._options.autoscroll,backdrop:this._options.backdrop,backdropContainer:this._options.backdropContainer,backdropPadding:this._options.backdropPadding,redirect:this._options.redirect,reflexElement:this._options.steps[e].element,backdropElement:this._options.steps[e].element,orphan:this._options.orphan,duration:this._options.duration,delay:this._options.delay,template:this._options.template,onShow:this._options.onShow,onShown:this._options.onShown,onHide:this._options.onHide,onHidden:this._options.onHidden,onNext:this._options.onNext,onPrev:this._options.onPrev,onPause:this._options.onPause,onResume:this._options.onResume,onRedirectError:this._options.onRedirectError},this._options.steps[e]):void 0},e.prototype.init=function(t){return this._force=t,this.ended()?(this._debug("Tour ended, init prevented."),this):(this.setCurrentStep(),this._initMouseNavigation(),this._initKeyboardNavigation(),this._onResize(function(t){return function(){return t.showStep(t._current)}}(this)),null!==this._current&&this.showStep(this._current),this._inited=!0,this)},e.prototype.start=function(t){var e;return null==t&&(t=!1),this._inited||this.init(t),null===this._current&&(e=this._makePromise(null!=this._options.onStart?this._options.onStart(this):void 0),this._callOnPromiseDone(e,this.showStep,0)),this},e.prototype.next=function(){var t;return t=this.hideStep(this._current,this._current+1),this._callOnPromiseDone(t,this._showNextStep)},e.prototype.prev=function(){var t;return t=this.hideStep(this._current,this._current-1),this._callOnPromiseDone(t,this._showPrevStep)},e.prototype.goTo=function(t){var e;return e=this.hideStep(this._current,t),this._callOnPromiseDone(e,this.showStep,t)},e.prototype.end=function(){var e,n;return e=function(e){return function(){return t(o).off("click.tour-"+e._options.name),t(o).off("keyup.tour-"+e._options.name),t(window).off("resize.tour-"+e._options.name),e._setState("end","yes"),e._inited=!1,e._force=!1,e._clearTimer(),null!=e._options.onEnd?e._options.onEnd(e):void 0}}(this),n=this.hideStep(this._current),this._callOnPromiseDone(n,e)},e.prototype.ended=function(){return!this._force&&!!this._getState("end")},e.prototype.restart=function(){return this._removeState("current_step"),this._removeState("end"),this._removeState("redirect_to"),this.start()},e.prototype.pause=function(){var t;return t=this.getStep(this._current),t&&t.duration?(this._paused=!0,this._duration-=(new Date).getTime()-this._start,window.clearTimeout(this._timer),this._debug("Paused/Stopped step "+(this._current+1)+" timer ("+this._duration+" remaining)."),null!=t.onPause?t.onPause(this,this._duration):void 0):this},e.prototype.resume=function(){var t;return t=this.getStep(this._current),t&&t.duration?(this._paused=!1,this._start=(new Date).getTime(),this._duration=this._duration||t.duration,this._timer=window.setTimeout(function(t){return function(){return t._isLast()?t.next():t.end()}}(this),this._duration),this._debug("Started step "+(this._current+1)+" timer with duration "+this._duration),null!=t.onResume&&this._duration!==t.duration?t.onResume(this,this._duration):void 0):this},e.prototype.hideStep=function(e,o){var n,i,r,s;return(s=this.getStep(e))?(this._clearTimer(),r=this._makePromise(null!=s.onHide?s.onHide(this,e):void 0),i=function(n){return function(){var i,r;return i=t(s.element),i.data("bs.popover")||i.data("popover")||(i=t("body")),i.popover("destroy").removeClass("tour-"+n._options.name+"-element tour-"+n._options.name+"-"+e+"-element").removeData("bs.popover").focus(),s.reflex&&t(s.reflexElement).removeClass("tour-step-element-reflex").off(""+n._reflexEvent(s.reflex)+".tour-"+n._options.name),s.backdrop&&(r=null!=o&&n.getStep(o),r&&r.backdrop&&r.backdropElement===s.backdropElement||n._hideBackdrop()),null!=s.onHidden?s.onHidden(n):void 0}}(this),n=s.delay.hide||s.delay,"[object Number]"==={}.toString.call(n)&&n>0?(this._debug("Wait "+n+" milliseconds to hide the step "+(this._current+1)),window.setTimeout(function(t){return function(){return t._callOnPromiseDone(r,i)}}(this),n)):this._callOnPromiseDone(r,i),r):void 0},e.prototype.showStep=function(t){var e,n,i,r,s,a;return this.ended()?(this._debug("Tour ended, showStep prevented."),this):(a=this.getStep(t),a&&(s=t<this._current,n=this._makePromise(null!=a.onShow?a.onShow(this,t):void 0),this.setCurrentStep(t),e=function(){switch({}.toString.call(a.path)){case"[object Function]":return a.path();case"[object String]":return this._options.basePath+a.path;default:return a.path}}.call(this),!a.redirect||!this._isRedirect(a.host,e,o.location)||(this._redirect(a,t,e),this._isJustPathHashDifferent(a.host,e,o.location)))?(r=function(e){return function(){var o;if(e._isOrphan(a)){if(a.orphan===!1)return e._debug("Skip the orphan step "+(e._current+1)+".\nOrphan option is false and the element does not exist or is hidden."),s?e._showPrevStep():e._showNextStep(),void 0;e._debug("Show the orphan step "+(e._current+1)+". Orphans option is true.")}return a.backdrop&&e._showBackdrop(a),o=function(){return e.getCurrentStep()!==t||e.ended()?void 0:(null!=a.element&&a.backdrop&&e._showOverlayElement(a,!0),e._showPopover(a,t),null!=a.onShown&&a.onShown(e),e._debug("Step "+(e._current+1)+" of "+e._options.steps.length))},a.autoscroll?e._scrollIntoView(a,o):o(),a.duration?e.resume():void 0}}(this),i=a.delay.show||a.delay,"[object Number]"==={}.toString.call(i)&&i>0?(this._debug("Wait "+i+" milliseconds to show the step "+(this._current+1)),window.setTimeout(function(t){return function(){return t._callOnPromiseDone(n,r)}}(this),i)):this._callOnPromiseDone(n,r),n):void 0)},e.prototype.getCurrentStep=function(){return this._current},e.prototype.setCurrentStep=function(t){return null!=t?(this._current=t,this._setState("current_step",t)):(this._current=this._getState("current_step"),this._current=null===this._current?null:parseInt(this._current,10)),this},e.prototype.redraw=function(){return this._showOverlayElement(this.getStep(this.getCurrentStep()).element,!0)},e.prototype._setState=function(t,e){var o,n;if(this._options.storage){n=""+this._options.name+"_"+t;try{this._options.storage.setItem(n,e)}catch(i){o=i,o.code===DOMException.QUOTA_EXCEEDED_ERR&&this._debug("LocalStorage quota exceeded. State storage failed.")}return this._options.afterSetState(n,e)}return null==this._state&&(this._state={}),this._state[t]=e},e.prototype._removeState=function(t){var e;return this._options.storage?(e=""+this._options.name+"_"+t,this._options.storage.removeItem(e),this._options.afterRemoveState(e)):null!=this._state?delete this._state[t]:void 0},e.prototype._getState=function(t){var e,o;return this._options.storage?(e=""+this._options.name+"_"+t,o=this._options.storage.getItem(e)):null!=this._state&&(o=this._state[t]),(void 0===o||"null"===o)&&(o=null),this._options.afterGetState(t,o),o},e.prototype._showNextStep=function(){var t,e,o;return o=this.getStep(this._current),e=function(t){return function(){return t.showStep(o.next)}}(this),t=this._makePromise(null!=o.onNext?o.onNext(this):void 0),this._callOnPromiseDone(t,e)},e.prototype._showPrevStep=function(){var t,e,o;return o=this.getStep(this._current),e=function(t){return function(){return t.showStep(o.prev)}}(this),t=this._makePromise(null!=o.onPrev?o.onPrev(this):void 0),this._callOnPromiseDone(t,e)},e.prototype._debug=function(t){return this._options.debug?window.console.log("Bootstrap Tour '"+this._options.name+"' | "+t):void 0},e.prototype._isRedirect=function(t,e,o){var n;return null!=t&&""!==t&&("[object RegExp]"==={}.toString.call(t)&&!t.test(o.origin)||"[object String]"==={}.toString.call(t)&&this._isHostDifferent(t,o))?!0:(n=[o.pathname,o.search,o.hash].join(""),null!=e&&""!==e&&("[object RegExp]"==={}.toString.call(e)&&!e.test(n)||"[object String]"==={}.toString.call(e)&&this._isPathDifferent(e,n)))},e.prototype._isHostDifferent=function(t,e){switch({}.toString.call(t)){case"[object RegExp]":return!t.test(e.origin);case"[object String]":return this._getProtocol(t)!==this._getProtocol(e.href)||this._getHost(t)!==this._getHost(e.href);default:return!0}},e.prototype._isPathDifferent=function(t,e){return this._getPath(t)!==this._getPath(e)||!this._equal(this._getQuery(t),this._getQuery(e))||!this._equal(this._getHash(t),this._getHash(e))},e.prototype._isJustPathHashDifferent=function(t,e,o){var n;return null!=t&&""!==t&&this._isHostDifferent(t,o)?!1:(n=[o.pathname,o.search,o.hash].join(""),"[object String]"==={}.toString.call(e)?this._getPath(e)===this._getPath(n)&&this._equal(this._getQuery(e),this._getQuery(n))&&!this._equal(this._getHash(e),this._getHash(n)):!1)},e.prototype._redirect=function(e,n,i){var r;return t.isFunction(e.redirect)?e.redirect.call(this,i):(r="[object String]"==={}.toString.call(e.host)?""+e.host+i:i,this._debug("Redirect to "+r),this._getState("redirect_to")!==""+n?(this._setState("redirect_to",""+n),o.location.href=r):(this._debug("Error redirection loop to "+i),this._removeState("redirect_to"),null!=e.onRedirectError?e.onRedirectError(this):void 0))},e.prototype._isOrphan=function(e){return null==e.element||!t(e.element).length||t(e.element).is(":hidden")&&"http://www.w3.org/2000/svg"!==t(e.element)[0].namespaceURI},e.prototype._isLast=function(){return this._current<this._options.steps.length-1},e.prototype._showPopover=function(e,o){var n,i,r,s,a;return t(".tour-"+this._options.name).remove(),s=t.extend({},this._options),r=this._isOrphan(e),e.template=this._template(e,o),r&&(e.element="body",e.placement="top"),n=t(e.element),n.addClass("tour-"+this._options.name+"-element tour-"+this._options.name+"-"+o+"-element"),e.options&&t.extend(s,e.options),e.reflex&&!r&&t(e.reflexElement).addClass("tour-step-element-reflex").off(""+this._reflexEvent(e.reflex)+".tour-"+this._options.name).on(""+this._reflexEvent(e.reflex)+".tour-"+this._options.name,function(t){return function(){return t._isLast()?t.next():t.end()}}(this)),a=e.smartPlacement===!0&&-1===e.placement.search(/auto/i),n.popover({placement:a?"auto "+e.placement:e.placement,trigger:"manual",title:e.title,content:e.content,html:!0,animation:e.animation,container:e.container,template:e.template,selector:e.element}).popover("show"),i=n.data("bs.popover")?n.data("bs.popover").tip():n.data("popover").tip(),i.attr("id",e.id),this._focus(i,n,e.next<0),this._reposition(i,e),r?this._center(i):void 0},e.prototype._template=function(e,o){var n,i,r,s,a,p;return p=e.template,this._isOrphan(e)&&"[object Boolean]"!=={}.toString.call(e.orphan)&&(p=e.orphan),a=t.isFunction(p)?t(p(o,e)):t(p),n=a.find(".popover-navigation"),r=n.find('[data-role="prev"]'),i=n.find('[data-role="next"]'),s=n.find('[data-role="pause-resume"]'),this._isOrphan(e)&&a.addClass("orphan"),a.addClass("tour-"+this._options.name+" tour-"+this._options.name+"-"+o),e.reflex&&a.addClass("tour-"+this._options.name+"-reflex"),e.prev<0&&r.addClass("disabled").prop("disabled",!0).prop("tabindex",-1),e.next<0&&i.addClass("disabled").prop("disabled",!0).prop("tabindex",-1),e.duration||s.remove(),a.clone().wrap("<div>").parent().html()},e.prototype._reflexEvent=function(t){return"[object Boolean]"==={}.toString.call(t)?"click":t},e.prototype._focus=function(t,e,o){var n,i;return i=o?"end":"next",n=t.find("[data-role='"+i+"']"),e.on("shown.bs.popover",function(){return n.focus()})},e.prototype._reposition=function(e,n){var i,r,s,a,p,h,l;if(a=e[0].offsetWidth,r=e[0].offsetHeight,l=e.offset(),p=l.left,h=l.top,i=t(o).outerHeight()-l.top-e.outerHeight(),0>i&&(l.top=l.top+i),s=t("html").outerWidth()-l.left-e.outerWidth(),0>s&&(l.left=l.left+s),l.top<0&&(l.top=0),l.left<0&&(l.left=0),e.offset(l),"bottom"===n.placement||"top"===n.placement){if(p!==l.left)return this._replaceArrow(e,2*(l.left-p),a,"left")}else if(h!==l.top)return this._replaceArrow(e,2*(l.top-h),r,"top")},e.prototype._center=function(e){return e.css("top",t(window).outerHeight()/2-e.outerHeight()/2)},e.prototype._replaceArrow=function(t,e,o,n){return t.find(".arrow").css(n,e?50*(1-e/o)+"%":"")},e.prototype._scrollIntoView=function(e,o){var n,i,r,s,a,p,h;if(n=t(e.element),!n.length)return o();switch(i=t(window),a=n.offset().top,s=n.outerHeight(),h=i.height(),p=0,e.placement){case"top":p=Math.max(0,a-h/2);break;case"left":case"right":p=Math.max(0,a+s/2-h/2);break;case"bottom":p=Math.max(0,a+s-h/2)}return this._debug("Scroll into view. ScrollTop: "+p+". Element offset: "+a+". Window height: "+h+"."),r=0,t("body, html").stop(!0,!0).animate({scrollTop:Math.ceil(p)},function(t){return function(){return 2===++r?(o(),t._debug("Scroll into view.\nAnimation end element offset: "+n.offset().top+".\nWindow height: "+i.height()+".")):void 0}}(this))},e.prototype._onResize=function(e,o){return t(window).on("resize.tour-"+this._options.name,function(){return clearTimeout(o),o=setTimeout(e,100)})},e.prototype._initMouseNavigation=function(){var e;return e=this,t(o).off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='prev']").off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='next']").off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='end']").off("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='pause-resume']").on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='next']",function(t){return function(e){return e.preventDefault(),t.next()}}(this)).on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='prev']",function(t){return function(e){return e.preventDefault(),t._current>0?t.prev():void 0}}(this)).on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='end']",function(t){return function(e){return e.preventDefault(),t.end()}}(this)).on("click.tour-"+this._options.name,".popover.tour-"+this._options.name+" *[data-role='pause-resume']",function(o){var n;return o.preventDefault(),n=t(this),n.text(e._paused?n.data("pause-text"):n.data("resume-text")),e._paused?e.resume():e.pause()})},e.prototype._initKeyboardNavigation=function(){return this._options.keyboard?t(o).on("keyup.tour-"+this._options.name,function(t){return function(e){if(e.which)switch(e.which){case 39:return e.preventDefault(),t._isLast()?t.next():t.end();case 37:if(e.preventDefault(),t._current>0)return t.prev()}}}(this)):void 0},e.prototype._makePromise=function(e){return e&&t.isFunction(e.then)?e:null},e.prototype._callOnPromiseDone=function(t,e,o){return t?t.then(function(t){return function(){return e.call(t,o)}}(this)):e.call(this,o)},e.prototype._showBackdrop=function(e){return this.backdrop.backgroundShown?void 0:(this.backdrop=t("<div>",{"class":"tour-backdrop"}),this.backdrop.backgroundShown=!0,t(e.backdropContainer).append(this.backdrop))},e.prototype._hideBackdrop=function(){return this._hideOverlayElement(),this._hideBackground()},e.prototype._hideBackground=function(){return this.backdrop&&this.backdrop.remove?(this.backdrop.remove(),this.backdrop.overlay=null,this.backdrop.backgroundShown=!1):void 0},e.prototype._showOverlayElement=function(e,o){var n,i,r;return i=t(e.element),n=t(e.backdropElement),!i||0===i.length||this.backdrop.overlayElementShown&&!o?void 0:(this.backdrop.overlayElementShown||(this.backdrop.$element=n.addClass("tour-step-backdrop"),this.backdrop.$background=t("<div>",{"class":"tour-step-background"}),this.backdrop.$background.appendTo(e.backdropContainer),this.backdrop.overlayElementShown=!0),r={width:n.innerWidth(),height:n.innerHeight(),offset:n.offset()},e.backdropPadding&&(r=this._applyBackdropPadding(e.backdropPadding,r)),this.backdrop.$background.width(r.width).height(r.height).offset(r.offset))},e.prototype._hideOverlayElement=function(){return this.backdrop.overlayElementShown?(this.backdrop.$element.removeClass("tour-step-backdrop"),this.backdrop.$background.remove(),this.backdrop.$element=null,this.backdrop.$background=null,this.backdrop.overlayElementShown=!1):void 0},e.prototype._applyBackdropPadding=function(t,e){return"object"==typeof t?(null==t.top&&(t.top=0),null==t.right&&(t.right=0),null==t.bottom&&(t.bottom=0),null==t.left&&(t.left=0),e.offset.top=e.offset.top-t.top,e.offset.left=e.offset.left-t.left,e.width=e.width+t.left+t.right,e.height=e.height+t.top+t.bottom):(e.offset.top=e.offset.top-t,e.offset.left=e.offset.left-t,e.width=e.width+2*t,e.height=e.height+2*t),e},e.prototype._clearTimer=function(){return window.clearTimeout(this._timer),this._timer=null,this._duration=null},e.prototype._getProtocol=function(t){return t=t.split("://"),t.length>1?t[0]:"http"},e.prototype._getHost=function(t){return t=t.split("//"),t=t.length>1?t[1]:t[0],t.split("/")[0]},e.prototype._getPath=function(t){return t.replace(/\/?$/,"").split("?")[0].split("#")[0]},e.prototype._getQuery=function(t){return this._getParams(t,"?")},e.prototype._getHash=function(t){return this._getParams(t,"#")},e.prototype._getParams=function(t,e){var o,n,i,r,s;if(n=t.split(e),1===n.length)return{};for(n=n[1].split("&"),i={},r=0,s=n.length;s>r;r++)o=n[r],o=o.split("="),i[o[0]]=o[1]||"";return i},e.prototype._equal=function(t,e){var o,n,i,r,s,a;if("[object Object]"==={}.toString.call(t)&&"[object Object]"==={}.toString.call(e)){if(n=Object.keys(t),i=Object.keys(e),n.length!==i.length)return!1;for(o in t)if(r=t[o],!this._equal(e[o],r))return!1;return!0}if("[object Array]"==={}.toString.call(t)&&"[object Array]"==={}.toString.call(e)){if(t.length!==e.length)return!1;for(o=s=0,a=t.length;a>s;o=++s)if(r=t[o],!this._equal(r,e[o]))return!1;return!0}return t===e},e}()});;
/*!
 * Bootstrap Colorpicker v2.5.2
 * https://itsjavi.com/bootstrap-colorpicker/
 */
!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(a){return b(a)}):"object"==typeof exports?module.exports=b(require("jquery")):jQuery&&!jQuery.fn.colorpicker&&b(jQuery)}(this,function(a){"use strict";var b=function(c,d,e,f,g){this.fallbackValue=e?"string"==typeof e?this.parse(e):e:null,this.fallbackFormat=f?f:"rgba",this.hexNumberSignPrefix=g===!0,this.value=this.fallbackValue,this.origFormat=null,this.predefinedColors=d?d:{},this.colors=a.extend({},b.webColors,this.predefinedColors),c&&("undefined"!=typeof c.h?this.value=c:this.setColor(String(c))),this.value||(this.value={h:0,s:0,b:0,a:1})};b.webColors={aliceblue:"f0f8ff",antiquewhite:"faebd7",aqua:"00ffff",aquamarine:"7fffd4",azure:"f0ffff",beige:"f5f5dc",bisque:"ffe4c4",black:"000000",blanchedalmond:"ffebcd",blue:"0000ff",blueviolet:"8a2be2",brown:"a52a2a",burlywood:"deb887",cadetblue:"5f9ea0",chartreuse:"7fff00",chocolate:"d2691e",coral:"ff7f50",cornflowerblue:"6495ed",cornsilk:"fff8dc",crimson:"dc143c",cyan:"00ffff",darkblue:"00008b",darkcyan:"008b8b",darkgoldenrod:"b8860b",darkgray:"a9a9a9",darkgreen:"006400",darkkhaki:"bdb76b",darkmagenta:"8b008b",darkolivegreen:"556b2f",darkorange:"ff8c00",darkorchid:"9932cc",darkred:"8b0000",darksalmon:"e9967a",darkseagreen:"8fbc8f",darkslateblue:"483d8b",darkslategray:"2f4f4f",darkturquoise:"00ced1",darkviolet:"9400d3",deeppink:"ff1493",deepskyblue:"00bfff",dimgray:"696969",dodgerblue:"1e90ff",firebrick:"b22222",floralwhite:"fffaf0",forestgreen:"228b22",fuchsia:"ff00ff",gainsboro:"dcdcdc",ghostwhite:"f8f8ff",gold:"ffd700",goldenrod:"daa520",gray:"808080",green:"008000",greenyellow:"adff2f",honeydew:"f0fff0",hotpink:"ff69b4",indianred:"cd5c5c",indigo:"4b0082",ivory:"fffff0",khaki:"f0e68c",lavender:"e6e6fa",lavenderblush:"fff0f5",lawngreen:"7cfc00",lemonchiffon:"fffacd",lightblue:"add8e6",lightcoral:"f08080",lightcyan:"e0ffff",lightgoldenrodyellow:"fafad2",lightgrey:"d3d3d3",lightgreen:"90ee90",lightpink:"ffb6c1",lightsalmon:"ffa07a",lightseagreen:"20b2aa",lightskyblue:"87cefa",lightslategray:"778899",lightsteelblue:"b0c4de",lightyellow:"ffffe0",lime:"00ff00",limegreen:"32cd32",linen:"faf0e6",magenta:"ff00ff",maroon:"800000",mediumaquamarine:"66cdaa",mediumblue:"0000cd",mediumorchid:"ba55d3",mediumpurple:"9370d8",mediumseagreen:"3cb371",mediumslateblue:"7b68ee",mediumspringgreen:"00fa9a",mediumturquoise:"48d1cc",mediumvioletred:"c71585",midnightblue:"191970",mintcream:"f5fffa",mistyrose:"ffe4e1",moccasin:"ffe4b5",navajowhite:"ffdead",navy:"000080",oldlace:"fdf5e6",olive:"808000",olivedrab:"6b8e23",orange:"ffa500",orangered:"ff4500",orchid:"da70d6",palegoldenrod:"eee8aa",palegreen:"98fb98",paleturquoise:"afeeee",palevioletred:"d87093",papayawhip:"ffefd5",peachpuff:"ffdab9",peru:"cd853f",pink:"ffc0cb",plum:"dda0dd",powderblue:"b0e0e6",purple:"800080",red:"ff0000",rosybrown:"bc8f8f",royalblue:"4169e1",saddlebrown:"8b4513",salmon:"fa8072",sandybrown:"f4a460",seagreen:"2e8b57",seashell:"fff5ee",sienna:"a0522d",silver:"c0c0c0",skyblue:"87ceeb",slateblue:"6a5acd",slategray:"708090",snow:"fffafa",springgreen:"00ff7f",steelblue:"4682b4",tan:"d2b48c",teal:"008080",thistle:"d8bfd8",tomato:"ff6347",turquoise:"40e0d0",violet:"ee82ee",wheat:"f5deb3",white:"ffffff",whitesmoke:"f5f5f5",yellow:"ffff00",yellowgreen:"9acd32",transparent:"transparent"},b.prototype={constructor:b,colors:{},predefinedColors:{},getValue:function(){return this.value},setValue:function(a){this.value=a},_sanitizeNumber:function(a){return"number"==typeof a?a:isNaN(a)||null===a||""===a||void 0===a?1:""===a?0:"undefined"!=typeof a.toLowerCase?(a.match(/^\./)&&(a="0"+a),Math.ceil(100*parseFloat(a))/100):1},isTransparent:function(a){return!(!a||!("string"==typeof a||a instanceof String))&&(a=a.toLowerCase().trim(),"transparent"===a||a.match(/#?00000000/)||a.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/))},rgbaIsTransparent:function(a){return 0===a.r&&0===a.g&&0===a.b&&0===a.a},setColor:function(a){if(a=a.toLowerCase().trim()){if(this.isTransparent(a))return this.value={h:0,s:0,b:0,a:0},!0;var b=this.parse(a);b?(this.value=this.value={h:b.h,s:b.s,b:b.b,a:b.a},this.origFormat||(this.origFormat=b.format)):this.fallbackValue&&(this.value=this.fallbackValue)}return!1},setHue:function(a){this.value.h=1-a},setSaturation:function(a){this.value.s=a},setBrightness:function(a){this.value.b=1-a},setAlpha:function(a){this.value.a=Math.round(parseInt(100*(1-a),10)/100*100)/100},toRGB:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a),a*=360;var e,f,g,h,i;return a=a%360/60,i=c*b,h=i*(1-Math.abs(a%2-1)),e=f=g=c-i,a=~~a,e+=[i,h,0,0,h,i][a],f+=[h,i,i,h,0,0][a],g+=[0,0,h,i,i,h][a],{r:Math.round(255*e),g:Math.round(255*f),b:Math.round(255*g),a:d}},toHex:function(a,b,c,d,e){arguments.length<=1&&(b=this.value.h,c=this.value.s,d=this.value.b,e=this.value.a);var f="#",g=this.toRGB(b,c,d,e);if(this.rgbaIsTransparent(g))return"transparent";a||(f=this.hexNumberSignPrefix?"#":"");var h=f+((1<<24)+(parseInt(g.r)<<16)+(parseInt(g.g)<<8)+parseInt(g.b)).toString(16).slice(1);return h},toHSL:function(a,b,c,d){0===arguments.length&&(a=this.value.h,b=this.value.s,c=this.value.b,d=this.value.a);var e=a,f=(2-b)*c,g=b*c;return g/=f>0&&f<=1?f:2-f,f/=2,g>1&&(g=1),{h:isNaN(e)?0:e,s:isNaN(g)?0:g,l:isNaN(f)?0:f,a:isNaN(d)?0:d}},toAlias:function(a,b,c,d){var e,f=0===arguments.length?this.toHex(!0):this.toHex(!0,a,b,c,d),g="alias"===this.origFormat?f:this.toString(!1,this.origFormat);for(var h in this.colors)if(e=this.colors[h].toLowerCase().trim(),e===f||e===g)return h;return!1},RGBtoHSB:function(a,b,c,d){a/=255,b/=255,c/=255;var e,f,g,h;return g=Math.max(a,b,c),h=g-Math.min(a,b,c),e=0===h?null:g===a?(b-c)/h:g===b?(c-a)/h+2:(a-b)/h+4,e=(e+360)%6*60/360,f=0===h?0:h/g,{h:this._sanitizeNumber(e),s:f,b:g,a:this._sanitizeNumber(d)}},HueToRGB:function(a,b,c){return c<0?c+=1:c>1&&(c-=1),6*c<1?a+(b-a)*c*6:2*c<1?b:3*c<2?a+(b-a)*(2/3-c)*6:a},HSLtoRGB:function(a,b,c,d){b<0&&(b=0);var e;e=c<=.5?c*(1+b):c+b-c*b;var f=2*c-e,g=a+1/3,h=a,i=a-1/3,j=Math.round(255*this.HueToRGB(f,e,g)),k=Math.round(255*this.HueToRGB(f,e,h)),l=Math.round(255*this.HueToRGB(f,e,i));return[j,k,l,this._sanitizeNumber(d)]},parse:function(b){if("string"!=typeof b)return this.fallbackValue;if(0===arguments.length)return!1;var c,d,e=this,f=!1,g="undefined"!=typeof this.colors[b];return g&&(b=this.colors[b].toLowerCase().trim()),a.each(this.stringParsers,function(a,h){var i=h.re.exec(b);return c=i&&h.parse.apply(e,[i]),!c||(f={},d=g?"alias":h.format?h.format:e.getValidFallbackFormat(),f=d.match(/hsla?/)?e.RGBtoHSB.apply(e,e.HSLtoRGB.apply(e,c)):e.RGBtoHSB.apply(e,c),f instanceof Object&&(f.format=d),!1)}),f},getValidFallbackFormat:function(){var a=["rgba","rgb","hex","hsla","hsl"];return this.origFormat&&a.indexOf(this.origFormat)!==-1?this.origFormat:this.fallbackFormat&&a.indexOf(this.fallbackFormat)!==-1?this.fallbackFormat:"rgba"},toString:function(a,c,d){c=c||this.origFormat||this.fallbackFormat,d=d||!1;var e=!1;switch(c){case"rgb":return e=this.toRGB(),this.rgbaIsTransparent(e)?"transparent":"rgb("+e.r+","+e.g+","+e.b+")";case"rgba":return e=this.toRGB(),"rgba("+e.r+","+e.g+","+e.b+","+e.a+")";case"hsl":return e=this.toHSL(),"hsl("+Math.round(360*e.h)+","+Math.round(100*e.s)+"%,"+Math.round(100*e.l)+"%)";case"hsla":return e=this.toHSL(),"hsla("+Math.round(360*e.h)+","+Math.round(100*e.s)+"%,"+Math.round(100*e.l)+"%,"+e.a+")";case"hex":return this.toHex(a);case"alias":return e=this.toAlias(),e===!1?this.toString(a,this.getValidFallbackFormat()):d&&!(e in b.webColors)&&e in this.predefinedColors?this.predefinedColors[e]:e;default:return e}},stringParsers:[{re:/rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,format:"rgb",parse:function(a){return[a[1],a[2],a[3],1]}},{re:/rgb\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,format:"rgb",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],1]}},{re:/rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[a[1],a[2],a[3],a[4]]}},{re:/rgba\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"rgba",parse:function(a){return[2.55*a[1],2.55*a[2],2.55*a[3],a[4]]}},{re:/hsl\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,format:"hsl",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/hsla\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,format:"hsla",parse:function(a){return[a[1]/360,a[2]/100,a[3]/100,a[4]]}},{re:/#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,format:"hex",parse:function(a){return[parseInt(a[1],16),parseInt(a[2],16),parseInt(a[3],16),1]}},{re:/#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,format:"hex",parse:function(a){return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16),1]}}],colorNameToHex:function(a){return"undefined"!=typeof this.colors[a.toLowerCase()]&&this.colors[a.toLowerCase()]}};var c={horizontal:!1,inline:!1,color:!1,format:!1,input:"input",container:!1,component:".add-on, .input-group-addon",fallbackColor:!1,fallbackFormat:"hex",hexNumberSignPrefix:!0,sliders:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setHue"},alpha:{maxLeft:0,maxTop:100,callLeft:!1,callTop:"setAlpha"}},slidersHorz:{saturation:{maxLeft:100,maxTop:100,callLeft:"setSaturation",callTop:"setBrightness"},hue:{maxLeft:100,maxTop:0,callLeft:"setHue",callTop:!1},alpha:{maxLeft:100,maxTop:0,callLeft:"setAlpha",callTop:!1}},template:'<div class="colorpicker dropdown-menu"><div class="colorpicker-saturation"><i><b></b></i></div><div class="colorpicker-hue"><i></i></div><div class="colorpicker-alpha"><i></i></div><div class="colorpicker-color"><div /></div><div class="colorpicker-selectors"></div></div>',align:"right",customClass:null,colorSelectors:null},d=function(b,d){this.element=a(b).addClass("colorpicker-element"),this.options=a.extend(!0,{},c,this.element.data(),d),this.component=this.options.component,this.component=this.component!==!1&&this.element.find(this.component),this.component&&0===this.component.length&&(this.component=!1),this.container=this.options.container===!0?this.element:this.options.container,this.container=this.container!==!1&&a(this.container),this.input=this.element.is("input")?this.element:!!this.options.input&&this.element.find(this.options.input),this.input&&0===this.input.length&&(this.input=!1),this.color=this.createColor(this.options.color!==!1?this.options.color:this.getValue()),this.format=this.options.format!==!1?this.options.format:this.color.origFormat,this.options.color!==!1&&(this.updateInput(this.color),this.updateData(this.color)),this.disabled=!1;var e=this.picker=a(this.options.template);if(this.options.customClass&&e.addClass(this.options.customClass),this.options.inline?e.addClass("colorpicker-inline colorpicker-visible"):e.addClass("colorpicker-hidden"),this.options.horizontal&&e.addClass("colorpicker-horizontal"),["rgba","hsla","alias"].indexOf(this.format)===-1&&this.options.format!==!1&&"transparent"!==this.getValue()||e.addClass("colorpicker-with-alpha"),"right"===this.options.align&&e.addClass("colorpicker-right"),this.options.inline===!0&&e.addClass("colorpicker-no-arrow"),this.options.colorSelectors){var f=this,g=f.picker.find(".colorpicker-selectors");g.length>0&&(a.each(this.options.colorSelectors,function(b,c){var d=a("<i />").addClass("colorpicker-selectors-color").css("background-color",c).data("class",b).data("alias",b);d.on("mousedown.colorpicker touchstart.colorpicker",function(b){b.preventDefault(),f.setValue("alias"===f.format?a(this).data("alias"):a(this).css("background-color"))}),g.append(d)}),g.show().addClass("colorpicker-visible"))}e.on("mousedown.colorpicker touchstart.colorpicker",a.proxy(function(a){a.target===a.currentTarget&&a.preventDefault()},this)),e.find(".colorpicker-saturation, .colorpicker-hue, .colorpicker-alpha").on("mousedown.colorpicker touchstart.colorpicker",a.proxy(this.mousedown,this)),e.appendTo(this.container?this.container:a("body")),this.input!==!1&&(this.input.on({"keyup.colorpicker":a.proxy(this.keyup,this)}),this.input.on({"change.colorpicker":a.proxy(this.change,this)}),this.component===!1&&this.element.on({"focus.colorpicker":a.proxy(this.show,this)}),this.options.inline===!1&&this.element.on({"focusout.colorpicker":a.proxy(this.hide,this)})),this.component!==!1&&this.component.on({"click.colorpicker":a.proxy(this.show,this)}),this.input===!1&&this.component===!1&&this.element.on({"click.colorpicker":a.proxy(this.show,this)}),this.input!==!1&&this.component!==!1&&"color"===this.input.attr("type")&&this.input.on({"click.colorpicker":a.proxy(this.show,this),"focus.colorpicker":a.proxy(this.show,this)}),this.update(),a(a.proxy(function(){this.element.trigger("create")},this))};d.Color=b,d.prototype={constructor:d,destroy:function(){this.picker.remove(),this.element.removeData("colorpicker","color").off(".colorpicker"),this.input!==!1&&this.input.off(".colorpicker"),this.component!==!1&&this.component.off(".colorpicker"),this.element.removeClass("colorpicker-element"),this.element.trigger({type:"destroy"})},reposition:function(){if(this.options.inline!==!1||this.options.container)return!1;var a=this.container&&this.container[0]!==window.document.body?"position":"offset",b=this.component||this.element,c=b[a]();"right"===this.options.align&&(c.left-=this.picker.outerWidth()-b.outerWidth()),this.picker.css({top:c.top+b.outerHeight(),left:c.left})},show:function(b){this.isDisabled()||(this.picker.addClass("colorpicker-visible").removeClass("colorpicker-hidden"),this.reposition(),a(window).on("resize.colorpicker",a.proxy(this.reposition,this)),!b||this.hasInput()&&"color"!==this.input.attr("type")||b.stopPropagation&&b.preventDefault&&(b.stopPropagation(),b.preventDefault()),!this.component&&this.input||this.options.inline!==!1||a(window.document).on({"mousedown.colorpicker":a.proxy(this.hide,this)}),this.element.trigger({type:"showPicker",color:this.color}))},hide:function(b){return("undefined"==typeof b||!b.target||!(a(b.currentTarget).parents(".colorpicker").length>0||a(b.target).parents(".colorpicker").length>0))&&(this.picker.addClass("colorpicker-hidden").removeClass("colorpicker-visible"),a(window).off("resize.colorpicker",this.reposition),a(window.document).off({"mousedown.colorpicker":this.hide}),this.update(),void this.element.trigger({type:"hidePicker",color:this.color}))},updateData:function(a){return a=a||this.color.toString(!1,this.format),this.element.data("color",a),a},updateInput:function(a){return a=a||this.color.toString(!1,this.format),this.input!==!1&&(this.input.prop("value",a),this.input.trigger("change")),a},updatePicker:function(a){"undefined"!=typeof a&&(this.color=this.createColor(a));var b=this.options.horizontal===!1?this.options.sliders:this.options.slidersHorz,c=this.picker.find("i");if(0!==c.length)return this.options.horizontal===!1?(b=this.options.sliders,c.eq(1).css("top",b.hue.maxTop*(1-this.color.value.h)).end().eq(2).css("top",b.alpha.maxTop*(1-this.color.value.a))):(b=this.options.slidersHorz,c.eq(1).css("left",b.hue.maxLeft*(1-this.color.value.h)).end().eq(2).css("left",b.alpha.maxLeft*(1-this.color.value.a))),c.eq(0).css({top:b.saturation.maxTop-this.color.value.b*b.saturation.maxTop,left:this.color.value.s*b.saturation.maxLeft}),this.picker.find(".colorpicker-saturation").css("backgroundColor",this.color.toHex(!0,this.color.value.h,1,1,1)),this.picker.find(".colorpicker-alpha").css("backgroundColor",this.color.toHex(!0)),this.picker.find(".colorpicker-color, .colorpicker-color div").css("backgroundColor",this.color.toString(!0,this.format)),a},updateComponent:function(a){var b;if(b="undefined"!=typeof a?this.createColor(a):this.color,this.component!==!1){var c=this.component.find("i").eq(0);c.length>0?c.css({backgroundColor:b.toString(!0,this.format)}):this.component.css({backgroundColor:b.toString(!0,this.format)})}return b.toString(!1,this.format)},update:function(a){var b;return this.getValue(!1)===!1&&a!==!0||(b=this.updateComponent(),this.updateInput(b),this.updateData(b),this.updatePicker()),b},setValue:function(a){this.color=this.createColor(a),this.update(!0),this.element.trigger({type:"changeColor",color:this.color,value:a})},createColor:function(a){return new b(a?a:null,this.options.colorSelectors,this.options.fallbackColor?this.options.fallbackColor:this.color,this.options.fallbackFormat,this.options.hexNumberSignPrefix)},getValue:function(a){a="undefined"==typeof a?this.options.fallbackColor:a;var b;return b=this.hasInput()?this.input.val():this.element.data("color"),void 0!==b&&""!==b&&null!==b||(b=a),b},hasInput:function(){return this.input!==!1},isDisabled:function(){return this.disabled},disable:function(){return this.hasInput()&&this.input.prop("disabled",!0),this.disabled=!0,this.element.trigger({type:"disable",color:this.color,value:this.getValue()}),!0},enable:function(){return this.hasInput()&&this.input.prop("disabled",!1),this.disabled=!1,this.element.trigger({type:"enable",color:this.color,value:this.getValue()}),!0},currentSlider:null,mousePointer:{left:0,top:0},mousedown:function(b){!b.pageX&&!b.pageY&&b.originalEvent&&b.originalEvent.touches&&(b.pageX=b.originalEvent.touches[0].pageX,b.pageY=b.originalEvent.touches[0].pageY),b.stopPropagation(),b.preventDefault();var c=a(b.target),d=c.closest("div"),e=this.options.horizontal?this.options.slidersHorz:this.options.sliders;if(!d.is(".colorpicker")){if(d.is(".colorpicker-saturation"))this.currentSlider=a.extend({},e.saturation);else if(d.is(".colorpicker-hue"))this.currentSlider=a.extend({},e.hue);else{if(!d.is(".colorpicker-alpha"))return!1;this.currentSlider=a.extend({},e.alpha)}var f=d.offset();this.currentSlider.guide=d.find("i")[0].style,this.currentSlider.left=b.pageX-f.left,this.currentSlider.top=b.pageY-f.top,this.mousePointer={left:b.pageX,top:b.pageY},a(window.document).on({"mousemove.colorpicker":a.proxy(this.mousemove,this),"touchmove.colorpicker":a.proxy(this.mousemove,this),"mouseup.colorpicker":a.proxy(this.mouseup,this),"touchend.colorpicker":a.proxy(this.mouseup,this)}).trigger("mousemove")}return!1},mousemove:function(a){!a.pageX&&!a.pageY&&a.originalEvent&&a.originalEvent.touches&&(a.pageX=a.originalEvent.touches[0].pageX,a.pageY=a.originalEvent.touches[0].pageY),a.stopPropagation(),a.preventDefault();var b=Math.max(0,Math.min(this.currentSlider.maxLeft,this.currentSlider.left+((a.pageX||this.mousePointer.left)-this.mousePointer.left))),c=Math.max(0,Math.min(this.currentSlider.maxTop,this.currentSlider.top+((a.pageY||this.mousePointer.top)-this.mousePointer.top)));return this.currentSlider.guide.left=b+"px",this.currentSlider.guide.top=c+"px",this.currentSlider.callLeft&&this.color[this.currentSlider.callLeft].call(this.color,b/this.currentSlider.maxLeft),this.currentSlider.callTop&&this.color[this.currentSlider.callTop].call(this.color,c/this.currentSlider.maxTop),this.options.format!==!1||"setAlpha"!==this.currentSlider.callTop&&"setAlpha"!==this.currentSlider.callLeft||(1!==this.color.value.a?(this.format="rgba",this.color.origFormat="rgba"):(this.format="hex",this.color.origFormat="hex")),this.update(!0),this.element.trigger({type:"changeColor",color:this.color}),!1},mouseup:function(b){return b.stopPropagation(),b.preventDefault(),a(window.document).off({"mousemove.colorpicker":this.mousemove,"touchmove.colorpicker":this.mousemove,"mouseup.colorpicker":this.mouseup,"touchend.colorpicker":this.mouseup}),!1},change:function(a){this.keyup(a)},keyup:function(a){38===a.keyCode?(this.color.value.a<1&&(this.color.value.a=Math.round(100*(this.color.value.a+.01))/100),this.update(!0)):40===a.keyCode?(this.color.value.a>0&&(this.color.value.a=Math.round(100*(this.color.value.a-.01))/100),this.update(!0)):(this.color=this.createColor(this.input.val()),this.color.origFormat&&this.options.format===!1&&(this.format=this.color.origFormat),this.getValue(!1)!==!1&&(this.updateData(),this.updateComponent(),this.updatePicker())),this.element.trigger({type:"changeColor",color:this.color,value:this.input.val()})}},a.colorpicker=d,a.fn.colorpicker=function(b){var c=Array.prototype.slice.call(arguments,1),e=1===this.length,f=null,g=this.each(function(){var e=a(this),g=e.data("colorpicker"),h="object"==typeof b?b:{};g||(g=new d(this,h),e.data("colorpicker",g)),"string"==typeof b?a.isFunction(g[b])?f=g[b].apply(g,c):(c.length&&(g[b]=c[0]),f=g[b]):f=e});return e?f:g},a.fn.colorpicker.constructor=d});;
!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){a.ui=a.ui||{};a.ui.version="1.12.1";!function(){function b(a,b,c){return[parseFloat(a[0])*(l.test(a[0])?b/100:1),parseFloat(a[1])*(l.test(a[1])?c/100:1)]}function c(b,c){return parseInt(a.css(b,c),10)||0}function d(b){var c=b[0];return 9===c.nodeType?{width:b.width(),height:b.height(),offset:{top:0,left:0}}:a.isWindow(c)?{width:b.width(),height:b.height(),offset:{top:b.scrollTop(),left:b.scrollLeft()}}:c.preventDefault?{width:0,height:0,offset:{top:c.pageY,left:c.pageX}}:{width:b.outerWidth(),height:b.outerHeight(),offset:b.offset()}}var e,f=Math.max,g=Math.abs,h=/left|center|right/,i=/top|center|bottom/,j=/[\+\-]\d+(\.[\d]+)?%?/,k=/^\w+/,l=/%$/,m=a.fn.pos;a.pos={scrollbarWidth:function(){if(void 0!==e)return e;var b,c,d=a("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),f=d.children()[0];return a("body").append(d),b=f.offsetWidth,d.css("overflow","scroll"),c=f.offsetWidth,b===c&&(c=d[0].clientWidth),d.remove(),e=b-c},getScrollInfo:function(b){var c=b.isWindow||b.isDocument?"":b.element.css("overflow-x"),d=b.isWindow||b.isDocument?"":b.element.css("overflow-y"),e="scroll"===c||"auto"===c&&b.width<b.element[0].scrollWidth;return{width:"scroll"===d||"auto"===d&&b.height<b.element[0].scrollHeight?a.pos.scrollbarWidth():0,height:e?a.pos.scrollbarWidth():0}},getWithinInfo:function(b){var c=a(b||window),d=a.isWindow(c[0]),e=!!c[0]&&9===c[0].nodeType;return{element:c,isWindow:d,isDocument:e,offset:d||e?{left:0,top:0}:a(b).offset(),scrollLeft:c.scrollLeft(),scrollTop:c.scrollTop(),width:c.outerWidth(),height:c.outerHeight()}}},a.fn.pos=function(e){if(!e||!e.of)return m.apply(this,arguments);e=a.extend({},e);var l,n,o,p,q,r,s=a(e.of),t=a.pos.getWithinInfo(e.within),u=a.pos.getScrollInfo(t),v=(e.collision||"flip").split(" "),w={};return r=d(s),s[0].preventDefault&&(e.at="left top"),n=r.width,o=r.height,p=r.offset,q=a.extend({},p),a.each(["my","at"],function(){var a,b,c=(e[this]||"").split(" ");1===c.length&&(c=h.test(c[0])?c.concat(["center"]):i.test(c[0])?["center"].concat(c):["center","center"]),c[0]=h.test(c[0])?c[0]:"center",c[1]=i.test(c[1])?c[1]:"center",a=j.exec(c[0]),b=j.exec(c[1]),w[this]=[a?a[0]:0,b?b[0]:0],e[this]=[k.exec(c[0])[0],k.exec(c[1])[0]]}),1===v.length&&(v[1]=v[0]),"right"===e.at[0]?q.left+=n:"center"===e.at[0]&&(q.left+=n/2),"bottom"===e.at[1]?q.top+=o:"center"===e.at[1]&&(q.top+=o/2),l=b(w.at,n,o),q.left+=l[0],q.top+=l[1],this.each(function(){var d,h,i=a(this),j=i.outerWidth(),k=i.outerHeight(),m=c(this,"marginLeft"),r=c(this,"marginTop"),x=j+m+c(this,"marginRight")+u.width,y=k+r+c(this,"marginBottom")+u.height,z=a.extend({},q),A=b(w.my,i.outerWidth(),i.outerHeight());"right"===e.my[0]?z.left-=j:"center"===e.my[0]&&(z.left-=j/2),"bottom"===e.my[1]?z.top-=k:"center"===e.my[1]&&(z.top-=k/2),z.left+=A[0],z.top+=A[1],d={marginLeft:m,marginTop:r},a.each(["left","top"],function(b,c){a.ui.pos[v[b]]&&a.ui.pos[v[b]][c](z,{targetWidth:n,targetHeight:o,elemWidth:j,elemHeight:k,collisionPosition:d,collisionWidth:x,collisionHeight:y,offset:[l[0]+A[0],l[1]+A[1]],my:e.my,at:e.at,within:t,elem:i})}),e.using&&(h=function(a){var b=p.left-z.left,c=b+n-j,d=p.top-z.top,h=d+o-k,l={target:{element:s,left:p.left,top:p.top,width:n,height:o},element:{element:i,left:z.left,top:z.top,width:j,height:k},horizontal:c<0?"left":b>0?"right":"center",vertical:h<0?"top":d>0?"bottom":"middle"};n<j&&g(b+c)<n&&(l.horizontal="center"),o<k&&g(d+h)<o&&(l.vertical="middle"),f(g(b),g(c))>f(g(d),g(h))?l.important="horizontal":l.important="vertical",e.using.call(this,a,l)}),i.offset(a.extend(z,{using:h}))})},a.ui.pos={_trigger:function(a,b,c,d){b.elem&&b.elem.trigger({type:c,position:a,positionData:b,triggered:d})},fit:{left:function(b,c){a.ui.pos._trigger(b,c,"posCollide","fitLeft");var d,e=c.within,g=e.isWindow?e.scrollLeft:e.offset.left,h=e.width,i=b.left-c.collisionPosition.marginLeft,j=g-i,k=i+c.collisionWidth-h-g;c.collisionWidth>h?j>0&&k<=0?(d=b.left+j+c.collisionWidth-h-g,b.left+=j-d):b.left=k>0&&j<=0?g:j>k?g+h-c.collisionWidth:g:j>0?b.left+=j:k>0?b.left-=k:b.left=f(b.left-i,b.left),a.ui.pos._trigger(b,c,"posCollided","fitLeft")},top:function(b,c){a.ui.pos._trigger(b,c,"posCollide","fitTop");var d,e=c.within,g=e.isWindow?e.scrollTop:e.offset.top,h=c.within.height,i=b.top-c.collisionPosition.marginTop,j=g-i,k=i+c.collisionHeight-h-g;c.collisionHeight>h?j>0&&k<=0?(d=b.top+j+c.collisionHeight-h-g,b.top+=j-d):b.top=k>0&&j<=0?g:j>k?g+h-c.collisionHeight:g:j>0?b.top+=j:k>0?b.top-=k:b.top=f(b.top-i,b.top),a.ui.pos._trigger(b,c,"posCollided","fitTop")}},flip:{left:function(b,c){a.ui.pos._trigger(b,c,"posCollide","flipLeft");var d,e,f=c.within,h=f.offset.left+f.scrollLeft,i=f.width,j=f.isWindow?f.scrollLeft:f.offset.left,k=b.left-c.collisionPosition.marginLeft,l=k-j,m=k+c.collisionWidth-i-j,n="left"===c.my[0]?-c.elemWidth:"right"===c.my[0]?c.elemWidth:0,o="left"===c.at[0]?c.targetWidth:"right"===c.at[0]?-c.targetWidth:0,p=-2*c.offset[0];l<0?((d=b.left+n+o+p+c.collisionWidth-i-h)<0||d<g(l))&&(b.left+=n+o+p):m>0&&((e=b.left-c.collisionPosition.marginLeft+n+o+p-j)>0||g(e)<m)&&(b.left+=n+o+p),a.ui.pos._trigger(b,c,"posCollided","flipLeft")},top:function(b,c){a.ui.pos._trigger(b,c,"posCollide","flipTop");var d,e,f=c.within,h=f.offset.top+f.scrollTop,i=f.height,j=f.isWindow?f.scrollTop:f.offset.top,k=b.top-c.collisionPosition.marginTop,l=k-j,m=k+c.collisionHeight-i-j,n="top"===c.my[1],o=n?-c.elemHeight:"bottom"===c.my[1]?c.elemHeight:0,p="top"===c.at[1]?c.targetHeight:"bottom"===c.at[1]?-c.targetHeight:0,q=-2*c.offset[1];l<0?((e=b.top+o+p+q+c.collisionHeight-i-h)<0||e<g(l))&&(b.top+=o+p+q):m>0&&((d=b.top-c.collisionPosition.marginTop+o+p+q-j)>0||g(d)<m)&&(b.top+=o+p+q),a.ui.pos._trigger(b,c,"posCollided","flipTop")}},flipfit:{left:function(){a.ui.pos.flip.left.apply(this,arguments),a.ui.pos.fit.left.apply(this,arguments)},top:function(){a.ui.pos.flip.top.apply(this,arguments),a.ui.pos.fit.top.apply(this,arguments)}}},function(){var b,c,d,e,f,g=document.getElementsByTagName("body")[0],h=document.createElement("div");b=document.createElement(g?"div":"body"),d={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},g&&a.extend(d,{position:"absolute",left:"-1000px",top:"-1000px"});for(f in d)b.style[f]=d[f];b.appendChild(h),c=g||document.documentElement,c.insertBefore(b,c.firstChild),h.style.cssText="position: absolute; left: 10.7432222px;",e=a(h).offset().left,a.support.offsetFractions=e>10&&e<11,b.innerHTML="",c.removeChild(b)}()}();a.ui.position}),function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):window.jQuery&&!window.jQuery.fn.iconpicker&&a(window.jQuery)}(function(a){"use strict";var b={isEmpty:function(a){return!1===a||""===a||null===a||void 0===a},isEmptyObject:function(a){return!0===this.isEmpty(a)||0===a.length},isElement:function(b){return a(b).length>0},isString:function(a){return"string"==typeof a||a instanceof String},isArray:function(b){return a.isArray(b)},inArray:function(b,c){return-1!==a.inArray(b,c)},throwError:function(a){throw"Font Awesome Icon Picker Exception: "+a}},c=function(d,e){this._id=c._idCounter++,this.element=a(d).addClass("iconpicker-element"),this._trigger("iconpickerCreate"),this.options=a.extend({},c.defaultOptions,this.element.data(),e),this.options.templates=a.extend({},c.defaultOptions.templates,this.options.templates),this.options.originalPlacement=this.options.placement,this.container=!!b.isElement(this.options.container)&&a(this.options.container),!1===this.container&&(this.element.is(".dropdown-toggle")?this.container=a("~ .dropdown-menu:first",this.element):this.container=this.element.is("input,textarea,button,.btn")?this.element.parent():this.element),this.container.addClass("iconpicker-container"),this.isDropdownMenu()&&(this.options.templates.search=!1,this.options.templates.buttons=!1,this.options.placement="inline"),this.input=!!this.element.is("input,textarea")&&this.element.addClass("iconpicker-input"),!1===this.input&&(this.input=this.container.find(this.options.input),this.input.is("input,textarea")||(this.input=!1)),this.component=this.isDropdownMenu()?this.container.parent().find(this.options.component):this.container.find(this.options.component),0===this.component.length?this.component=!1:this.component.find("i").addClass("iconpicker-component"),this._createPopover(),this._createIconpicker(),0===this.getAcceptButton().length&&(this.options.mustAccept=!1),this.isInputGroup()?this.container.parent().append(this.popover):this.container.append(this.popover),this._bindElementEvents(),this._bindWindowEvents(),this.update(this.options.selected),this.isInline()&&this.show(),this._trigger("iconpickerCreated")};c._idCounter=0,c.defaultOptions={title:!1,selected:!1,defaultValue:!1,placement:"bottom",collision:"none",animation:!0,hideOnSelect:!1,showFooter:!1,searchInFooter:!1,mustAccept:!1,selectedCustomClass:"bg-primary",icons:[],fullClassFormatter:function(a){return"fa "+a},input:"input,.iconpicker-input",inputSearch:!1,container:!1,component:".input-group-addon,.iconpicker-component",templates:{popover:'<div class="iconpicker-popover popover"><div class="arrow"></div><div class="popover-title"></div><div class="popover-content"></div></div>',footer:'<div class="popover-footer"></div>',buttons:'<button class="iconpicker-btn iconpicker-btn-cancel btn btn-default btn-sm">Cancel</button> <button class="iconpicker-btn iconpicker-btn-accept btn btn-primary btn-sm">Accept</button>',search:'<input type="search" class="form-control iconpicker-search" placeholder="Type to filter" />',iconpicker:'<div class="iconpicker"><div class="iconpicker-items"></div></div>',iconpickerItem:'<a role="button" href="#" class="iconpicker-item"><i></i></a>'}},c.batch=function(b,c){var d=Array.prototype.slice.call(arguments,2);return a(b).each(function(){var b=a(this).data("iconpicker");b&&b[c].apply(b,d)})},c.prototype={constructor:c,options:{},_id:0,_trigger:function(b,c){c=c||{},this.element.trigger(a.extend({type:b,iconpickerInstance:this},c))},_createPopover:function(){this.popover=a(this.options.templates.popover);var c=this.popover.find(".popover-title");if(this.options.title&&c.append(a('<div class="popover-title-text">'+this.options.title+"</div>")),this.hasSeparatedSearchInput()&&!this.options.searchInFooter?c.append(this.options.templates.search):this.options.title||c.remove(),this.options.showFooter&&!b.isEmpty(this.options.templates.footer)){var d=a(this.options.templates.footer);this.hasSeparatedSearchInput()&&this.options.searchInFooter&&d.append(a(this.options.templates.search)),b.isEmpty(this.options.templates.buttons)||d.append(a(this.options.templates.buttons)),this.popover.append(d)}return!0===this.options.animation&&this.popover.addClass("fade"),this.popover},_createIconpicker:function(){var b=this;this.iconpicker=a(this.options.templates.iconpicker);var c=function(c){var d=a(this);return d.is("i")&&(d=d.parent()),b._trigger("iconpickerSelect",{iconpickerItem:d,iconpickerValue:b.iconpickerValue}),!1===b.options.mustAccept?(b.update(d.data("iconpickerValue")),b._trigger("iconpickerSelected",{iconpickerItem:this,iconpickerValue:b.iconpickerValue})):b.update(d.data("iconpickerValue"),!0),b.options.hideOnSelect&&!1===b.options.mustAccept&&b.hide(),c.preventDefault(),!1};for(var d in this.options.icons)if("string"==typeof this.options.icons[d]){var e=a(this.options.templates.iconpickerItem);e.find("i").addClass(this.options.fullClassFormatter(this.options.icons[d])),e.data("iconpickerValue",this.options.icons[d]).on("click.iconpicker",c),this.iconpicker.find(".iconpicker-items").append(e.attr("title","."+this.options.icons[d]))}return this.popover.find(".popover-content").append(this.iconpicker),this.iconpicker},_isEventInsideIconpicker:function(b){var c=a(b.target);return!((!c.hasClass("iconpicker-element")||c.hasClass("iconpicker-element")&&!c.is(this.element))&&0===c.parents(".iconpicker-popover").length)},_bindElementEvents:function(){var c=this;this.getSearchInput().on("keyup.iconpicker",function(){c.filter(a(this).val().toLowerCase())}),this.getAcceptButton().on("click.iconpicker",function(){var a=c.iconpicker.find(".iconpicker-selected").get(0);c.update(c.iconpickerValue),c._trigger("iconpickerSelected",{iconpickerItem:a,iconpickerValue:c.iconpickerValue}),c.isInline()||c.hide()}),this.getCancelButton().on("click.iconpicker",function(){c.isInline()||c.hide()}),this.element.on("focus.iconpicker",function(a){c.show(),a.stopPropagation()}),this.hasComponent()&&this.component.on("click.iconpicker",function(){c.toggle()}),this.hasInput()&&this.input.on("keyup.iconpicker",function(d){b.inArray(d.keyCode,[38,40,37,39,16,17,18,9,8,91,93,20,46,186,190,46,78,188,44,86])?c._updateFormGroupStatus(!1!==c.getValid(this.value)):c.update(),!0===c.options.inputSearch&&c.filter(a(this).val().toLowerCase())})},_bindWindowEvents:function(){var b=a(window.document),c=this,d=".iconpicker.inst"+this._id;return a(window).on("resize.iconpicker"+d+" orientationchange.iconpicker"+d,function(a){c.popover.hasClass("in")&&c.updatePlacement()}),c.isInline()||b.on("mouseup"+d,function(a){return c._isEventInsideIconpicker(a)||c.isInline()||c.hide(),a.stopPropagation(),a.preventDefault(),!1}),!1},_unbindElementEvents:function(){this.popover.off(".iconpicker"),this.element.off(".iconpicker"),this.hasInput()&&this.input.off(".iconpicker"),this.hasComponent()&&this.component.off(".iconpicker"),this.hasContainer()&&this.container.off(".iconpicker")},_unbindWindowEvents:function(){a(window).off(".iconpicker.inst"+this._id),a(window.document).off(".iconpicker.inst"+this._id)},updatePlacement:function(b,c){b=b||this.options.placement,this.options.placement=b,c=c||this.options.collision,c=!0===c?"flip":c;var d={at:"right bottom",my:"right top",of:this.hasInput()&&!this.isInputGroup()?this.input:this.container,collision:!0===c?"flip":c,within:window};if(this.popover.removeClass("inline topLeftCorner topLeft top topRight topRightCorner rightTop right rightBottom bottomRight bottomRightCorner bottom bottomLeft bottomLeftCorner leftBottom left leftTop"),"object"==typeof b)return this.popover.pos(a.extend({},d,b));switch(b){case"inline":d=!1;break;case"topLeftCorner":d.my="right bottom",d.at="left top";break;case"topLeft":d.my="left bottom",d.at="left top";break;case"top":d.my="center bottom",d.at="center top";break;case"topRight":d.my="right bottom",d.at="right top";break;case"topRightCorner":d.my="left bottom",d.at="right top";break;case"rightTop":d.my="left bottom",d.at="right center";break;case"right":d.my="left center",d.at="right center";break;case"rightBottom":d.my="left top",d.at="right center";break;case"bottomRightCorner":d.my="left top",d.at="right bottom";break;case"bottomRight":d.my="right top",d.at="right bottom";break;case"bottom":d.my="center top",d.at="center bottom";break;case"bottomLeft":d.my="left top",d.at="left bottom";break;case"bottomLeftCorner":d.my="right top",d.at="left bottom";break;case"leftBottom":d.my="right top",d.at="left center";break;case"left":d.my="right center",d.at="left center";break;case"leftTop":d.my="right bottom",d.at="left center";break;default:return!1}return this.popover.css({display:"inline"===this.options.placement?"":"block"}),!1!==d?this.popover.pos(d).css("maxWidth",a(window).width()-this.container.offset().left-5):this.popover.css({top:"auto",right:"auto",bottom:"auto",left:"auto",maxWidth:"none"}),this.popover.addClass(this.options.placement),!0},_updateComponents:function(){if(this.iconpicker.find(".iconpicker-item.iconpicker-selected").removeClass("iconpicker-selected "+this.options.selectedCustomClass),this.iconpickerValue&&this.iconpicker.find("."+this.options.fullClassFormatter(this.iconpickerValue).replace(/ /g,".")).parent().addClass("iconpicker-selected "+this.options.selectedCustomClass),this.hasComponent()){var a=this.component.find("i");a.length>0?a.attr("class",this.options.fullClassFormatter(this.iconpickerValue)):this.component.html(this.getHtml())}},_updateFormGroupStatus:function(a){return!!this.hasInput()&&(!1!==a?this.input.parents(".form-group:first").removeClass("has-error"):this.input.parents(".form-group:first").addClass("has-error"),!0)},getValid:function(c){b.isString(c)||(c="");var d=""===c;return c=a.trim(c),!(!b.inArray(c,this.options.icons)&&!d)&&c},setValue:function(a){var b=this.getValid(a);return!1!==b?(this.iconpickerValue=b,this._trigger("iconpickerSetValue",{iconpickerValue:b}),this.iconpickerValue):(this._trigger("iconpickerInvalid",{iconpickerValue:a}),!1)},getHtml:function(){return'<i class="'+this.options.fullClassFormatter(this.iconpickerValue)+'"></i>'},setSourceValue:function(a){return a=this.setValue(a),!1!==a&&""!==a&&(this.hasInput()?this.input.val(this.iconpickerValue):this.element.data("iconpickerValue",this.iconpickerValue),this._trigger("iconpickerSetSourceValue",{iconpickerValue:a})),a},getSourceValue:function(a){a=a||this.options.defaultValue;var b=a;return b=this.hasInput()?this.input.val():this.element.data("iconpickerValue"),void 0!==b&&""!==b&&null!==b&&!1!==b||(b=a),b},hasInput:function(){return!1!==this.input},isInputSearch:function(){return this.hasInput()&&!0===this.options.inputSearch},isInputGroup:function(){return this.container.is(".input-group")},isDropdownMenu:function(){return this.container.is(".dropdown-menu")},hasSeparatedSearchInput:function(){return!1!==this.options.templates.search&&!this.isInputSearch()},hasComponent:function(){return!1!==this.component},hasContainer:function(){return!1!==this.container},getAcceptButton:function(){return this.popover.find(".iconpicker-btn-accept")},getCancelButton:function(){return this.popover.find(".iconpicker-btn-cancel")},getSearchInput:function(){return this.popover.find(".iconpicker-search")},filter:function(c){if(b.isEmpty(c))return this.iconpicker.find(".iconpicker-item").show(),a(!1);var d=[];return this.iconpicker.find(".iconpicker-item").each(function(){var b=a(this),e=b.attr("title").toLowerCase(),f=!1;try{f=new RegExp(c,"g")}catch(a){f=!1}!1!==f&&e.match(f)?(d.push(b),b.show()):b.hide()}),d},show:function(){if(this.popover.hasClass("in"))return!1;a.iconpicker.batch(a(".iconpicker-popover.in:not(.inline)").not(this.popover),"hide"),this._trigger("iconpickerShow"),this.updatePlacement(),this.popover.addClass("in"),setTimeout(a.proxy(function(){this.popover.css("display",this.isInline()?"":"block"),this._trigger("iconpickerShown")},this),this.options.animation?300:1)},hide:function(){if(!this.popover.hasClass("in"))return!1;this._trigger("iconpickerHide"),this.popover.removeClass("in"),setTimeout(a.proxy(function(){this.popover.css("display","none"),this.getSearchInput().val(""),this.filter(""),this._trigger("iconpickerHidden")},this),this.options.animation?300:1)},toggle:function(){this.popover.is(":visible")?this.hide():this.show(!0)},update:function(a,b){return a=a||this.getSourceValue(this.iconpickerValue),this._trigger("iconpickerUpdate"),!0===b?a=this.setValue(a):(a=this.setSourceValue(a),this._updateFormGroupStatus(!1!==a)),!1!==a&&this._updateComponents(),this._trigger("iconpickerUpdated"),a},destroy:function(){this._trigger("iconpickerDestroy"),this.element.removeData("iconpicker").removeData("iconpickerValue").removeClass("iconpicker-element"),this._unbindElementEvents(),this._unbindWindowEvents(),a(this.popover).remove(),this._trigger("iconpickerDestroyed")},disable:function(){return!!this.hasInput()&&(this.input.prop("disabled",!0),!0)},enable:function(){return!!this.hasInput()&&(this.input.prop("disabled",!1),!0)},isDisabled:function(){return!!this.hasInput()&&!0===this.input.prop("disabled")},isInline:function(){return"inline"===this.options.placement||this.popover.hasClass("inline")}},a.iconpicker=c,a.fn.iconpicker=function(b){return this.each(function(){var d=a(this);d.data("iconpicker")||d.data("iconpicker",new c(this,"object"==typeof b?b:{}))})},c.defaultOptions.icons=["fa-500px","fa-address-book","fa-address-book-o","fa-address-card","fa-address-card-o","fa-adjust","fa-adn","fa-align-center","fa-align-justify","fa-align-left","fa-align-right","fa-amazon","fa-ambulance","fa-american-sign-language-interpreting","fa-anchor","fa-android","fa-angellist","fa-angle-double-down","fa-angle-double-left","fa-angle-double-right","fa-angle-double-up","fa-angle-down","fa-angle-left","fa-angle-right","fa-angle-up","fa-apple","fa-archive","fa-area-chart","fa-arrow-circle-down","fa-arrow-circle-left","fa-arrow-circle-o-down","fa-arrow-circle-o-left","fa-arrow-circle-o-right","fa-arrow-circle-o-up","fa-arrow-circle-right","fa-arrow-circle-up","fa-arrow-down","fa-arrow-left","fa-arrow-right","fa-arrow-up","fa-arrows","fa-arrows-alt","fa-arrows-h","fa-arrows-v","fa-asl-interpreting","fa-assistive-listening-systems","fa-asterisk","fa-at","fa-audio-description","fa-automobile","fa-backward","fa-balance-scale","fa-ban","fa-bandcamp","fa-bank","fa-bar-chart","fa-bar-chart-o","fa-barcode","fa-bars","fa-bath","fa-bathtub","fa-battery","fa-battery-0","fa-battery-1","fa-battery-2","fa-battery-3","fa-battery-4","fa-battery-empty","fa-battery-full","fa-battery-half","fa-battery-quarter","fa-battery-three-quarters","fa-bed","fa-beer","fa-behance","fa-behance-square","fa-bell","fa-bell-o","fa-bell-slash","fa-bell-slash-o","fa-bicycle","fa-binoculars","fa-birthday-cake","fa-bitbucket","fa-bitbucket-square","fa-bitcoin","fa-black-tie","fa-blind","fa-bluetooth","fa-bluetooth-b","fa-bold","fa-bolt","fa-bomb","fa-book","fa-bookmark","fa-bookmark-o","fa-braille","fa-briefcase","fa-btc","fa-bug","fa-building","fa-building-o","fa-bullhorn","fa-bullseye","fa-bus","fa-buysellads","fa-cab","fa-calculator","fa-calendar","fa-calendar-check-o","fa-calendar-minus-o","fa-calendar-o","fa-calendar-plus-o","fa-calendar-times-o","fa-camera","fa-camera-retro","fa-car","fa-caret-down","fa-caret-left","fa-caret-right","fa-caret-square-o-down","fa-caret-square-o-left","fa-caret-square-o-right","fa-caret-square-o-up","fa-caret-up","fa-cart-arrow-down","fa-cart-plus","fa-cc","fa-cc-amex","fa-cc-diners-club","fa-cc-discover","fa-cc-jcb","fa-cc-mastercard","fa-cc-paypal","fa-cc-stripe","fa-cc-visa","fa-certificate","fa-chain","fa-chain-broken","fa-check","fa-check-circle","fa-check-circle-o","fa-check-square","fa-check-square-o","fa-chevron-circle-down","fa-chevron-circle-left","fa-chevron-circle-right","fa-chevron-circle-up","fa-chevron-down","fa-chevron-left","fa-chevron-right","fa-chevron-up","fa-child","fa-chrome","fa-circle","fa-circle-o","fa-circle-o-notch","fa-circle-thin","fa-clipboard","fa-clock-o","fa-clone","fa-close","fa-cloud","fa-cloud-download","fa-cloud-upload","fa-cny","fa-code","fa-code-fork","fa-codepen","fa-codiepie","fa-coffee","fa-cog","fa-cogs","fa-columns","fa-comment","fa-comment-o","fa-commenting","fa-commenting-o","fa-comments","fa-comments-o","fa-compass","fa-compress","fa-connectdevelop","fa-contao","fa-copy","fa-copyright","fa-creative-commons","fa-credit-card","fa-credit-card-alt","fa-crop","fa-crosshairs","fa-css3","fa-cube","fa-cubes","fa-cut","fa-cutlery","fa-dashboard","fa-dashcube","fa-database","fa-deaf","fa-deafness","fa-dedent","fa-delicious","fa-desktop","fa-deviantart","fa-diamond","fa-digg","fa-dollar","fa-dot-circle-o","fa-download","fa-dribbble","fa-drivers-license","fa-drivers-license-o","fa-dropbox","fa-drupal","fa-edge","fa-edit","fa-eercast","fa-eject","fa-ellipsis-h","fa-ellipsis-v","fa-empire","fa-envelope","fa-envelope-o","fa-envelope-open","fa-envelope-open-o","fa-envelope-square","fa-envira","fa-eraser","fa-etsy","fa-eur","fa-euro","fa-exchange","fa-exclamation","fa-exclamation-circle","fa-exclamation-triangle","fa-expand","fa-expeditedssl","fa-external-link","fa-external-link-square","fa-eye","fa-eye-slash","fa-eyedropper","fa-fa","fa-facebook","fa-facebook-f","fa-facebook-official","fa-facebook-square","fa-fast-backward","fa-fast-forward","fa-fax","fa-feed","fa-female","fa-fighter-jet","fa-file","fa-file-archive-o","fa-file-audio-o","fa-file-code-o","fa-file-excel-o","fa-file-image-o","fa-file-movie-o","fa-file-o","fa-file-pdf-o","fa-file-photo-o","fa-file-picture-o","fa-file-powerpoint-o","fa-file-sound-o","fa-file-text","fa-file-text-o","fa-file-video-o","fa-file-word-o","fa-file-zip-o","fa-files-o","fa-film","fa-filter","fa-fire","fa-fire-extinguisher","fa-firefox","fa-first-order","fa-flag","fa-flag-checkered","fa-flag-o","fa-flash","fa-flask","fa-flickr","fa-floppy-o","fa-folder","fa-folder-o","fa-folder-open","fa-folder-open-o","fa-font","fa-font-awesome","fa-fonticons","fa-fort-awesome","fa-forumbee","fa-forward","fa-foursquare","fa-free-code-camp","fa-frown-o","fa-futbol-o","fa-gamepad","fa-gavel","fa-gbp","fa-ge","fa-gear","fa-gears","fa-genderless","fa-get-pocket","fa-gg","fa-gg-circle","fa-gift","fa-git","fa-git-square","fa-github","fa-github-alt","fa-github-square","fa-gitlab","fa-gittip","fa-glass","fa-glide","fa-glide-g","fa-globe","fa-google","fa-google-plus","fa-google-plus-circle","fa-google-plus-official","fa-google-plus-square","fa-google-wallet","fa-graduation-cap","fa-gratipay","fa-grav","fa-group","fa-h-square","fa-hacker-news","fa-hand-grab-o","fa-hand-lizard-o","fa-hand-o-down","fa-hand-o-left","fa-hand-o-right","fa-hand-o-up","fa-hand-paper-o","fa-hand-peace-o","fa-hand-pointer-o","fa-hand-rock-o","fa-hand-scissors-o","fa-hand-spock-o","fa-hand-stop-o","fa-handshake-o","fa-hard-of-hearing","fa-hashtag","fa-hdd-o","fa-header","fa-headphones","fa-heart","fa-heart-o","fa-heartbeat","fa-history","fa-home","fa-hospital-o","fa-hotel","fa-hourglass","fa-hourglass-1","fa-hourglass-2","fa-hourglass-3","fa-hourglass-end","fa-hourglass-half","fa-hourglass-o","fa-hourglass-start","fa-houzz","fa-html5","fa-i-cursor","fa-id-badge","fa-id-card","fa-id-card-o","fa-ils","fa-image","fa-imdb","fa-inbox","fa-indent","fa-industry","fa-info","fa-info-circle","fa-inr","fa-instagram","fa-institution","fa-internet-explorer","fa-intersex","fa-ioxhost","fa-italic","fa-joomla","fa-jpy","fa-jsfiddle","fa-key","fa-keyboard-o","fa-krw","fa-language","fa-laptop","fa-lastfm","fa-lastfm-square","fa-leaf","fa-leanpub","fa-legal","fa-lemon-o","fa-level-down","fa-level-up","fa-life-bouy","fa-life-buoy","fa-life-ring","fa-life-saver","fa-lightbulb-o","fa-line-chart","fa-link","fa-linkedin","fa-linkedin-square","fa-linode","fa-linux","fa-list","fa-list-alt","fa-list-ol","fa-list-ul","fa-location-arrow","fa-lock","fa-long-arrow-down","fa-long-arrow-left","fa-long-arrow-right","fa-long-arrow-up","fa-low-vision","fa-magic","fa-magnet","fa-mail-forward","fa-mail-reply","fa-mail-reply-all","fa-male","fa-map","fa-map-marker","fa-map-o","fa-map-pin","fa-map-signs","fa-mars","fa-mars-double","fa-mars-stroke","fa-mars-stroke-h","fa-mars-stroke-v","fa-maxcdn","fa-meanpath","fa-medium","fa-medkit","fa-meetup","fa-meh-o","fa-mercury","fa-microchip","fa-microphone","fa-microphone-slash","fa-minus","fa-minus-circle","fa-minus-square","fa-minus-square-o","fa-mixcloud","fa-mobile","fa-mobile-phone","fa-modx","fa-money","fa-moon-o","fa-mortar-board","fa-motorcycle","fa-mouse-pointer","fa-music","fa-navicon","fa-neuter","fa-newspaper-o","fa-object-group","fa-object-ungroup","fa-odnoklassniki","fa-odnoklassniki-square","fa-opencart","fa-openid","fa-opera","fa-optin-monster","fa-outdent","fa-pagelines","fa-paint-brush","fa-paper-plane","fa-paper-plane-o","fa-paperclip","fa-paragraph","fa-paste","fa-pause","fa-pause-circle","fa-pause-circle-o","fa-paw","fa-paypal","fa-pencil","fa-pencil-square","fa-pencil-square-o","fa-percent","fa-phone","fa-phone-square","fa-photo","fa-picture-o","fa-pie-chart","fa-pied-piper","fa-pied-piper-alt","fa-pied-piper-pp","fa-pinterest","fa-pinterest-p","fa-pinterest-square","fa-plane","fa-play","fa-play-circle","fa-play-circle-o","fa-plug","fa-plus","fa-plus-circle","fa-plus-square","fa-plus-square-o","fa-podcast","fa-power-off","fa-print","fa-product-hunt","fa-puzzle-piece","fa-qq","fa-qrcode","fa-question","fa-question-circle","fa-question-circle-o","fa-quora","fa-quote-left","fa-quote-right","fa-ra","fa-random","fa-ravelry","fa-rebel","fa-recycle","fa-reddit","fa-reddit-alien","fa-reddit-square","fa-refresh","fa-registered","fa-remove","fa-renren","fa-reorder","fa-repeat","fa-reply","fa-reply-all","fa-resistance","fa-retweet","fa-rmb","fa-road","fa-rocket","fa-rotate-left","fa-rotate-right","fa-rouble","fa-rss","fa-rss-square","fa-rub","fa-ruble","fa-rupee","fa-s15","fa-safari","fa-save","fa-scissors","fa-scribd","fa-search","fa-search-minus","fa-search-plus","fa-sellsy","fa-send","fa-send-o","fa-server","fa-share","fa-share-alt","fa-share-alt-square","fa-share-square","fa-share-square-o","fa-shekel","fa-sheqel","fa-shield","fa-ship","fa-shirtsinbulk","fa-shopping-bag","fa-shopping-basket","fa-shopping-cart","fa-shower","fa-sign-in","fa-sign-language","fa-sign-out","fa-signal","fa-signing","fa-simplybuilt","fa-sitemap","fa-skyatlas","fa-skype","fa-slack","fa-sliders","fa-slideshare","fa-smile-o","fa-snapchat","fa-snapchat-ghost","fa-snapchat-square","fa-snowflake-o","fa-soccer-ball-o","fa-sort","fa-sort-alpha-asc","fa-sort-alpha-desc","fa-sort-amount-asc","fa-sort-amount-desc","fa-sort-asc","fa-sort-desc","fa-sort-down","fa-sort-numeric-asc","fa-sort-numeric-desc","fa-sort-up","fa-soundcloud","fa-space-shuttle","fa-spinner","fa-spoon","fa-spotify","fa-square","fa-square-o","fa-stack-exchange","fa-stack-overflow","fa-star","fa-star-half","fa-star-half-empty","fa-star-half-full","fa-star-half-o","fa-star-o","fa-steam","fa-steam-square","fa-step-backward","fa-step-forward","fa-stethoscope","fa-sticky-note","fa-sticky-note-o","fa-stop","fa-stop-circle","fa-stop-circle-o","fa-street-view","fa-strikethrough","fa-stumbleupon","fa-stumbleupon-circle","fa-subscript","fa-subway","fa-suitcase","fa-sun-o","fa-superpowers","fa-superscript","fa-support","fa-table","fa-tablet","fa-tachometer","fa-tag","fa-tags","fa-tasks","fa-taxi","fa-telegram","fa-television","fa-tencent-weibo","fa-terminal","fa-text-height","fa-text-width","fa-th","fa-th-large","fa-th-list","fa-themeisle","fa-thermometer","fa-thermometer-0","fa-thermometer-1","fa-thermometer-2","fa-thermometer-3","fa-thermometer-4","fa-thermometer-empty","fa-thermometer-full","fa-thermometer-half","fa-thermometer-quarter","fa-thermometer-three-quarters","fa-thumb-tack","fa-thumbs-down","fa-thumbs-o-down","fa-thumbs-o-up","fa-thumbs-up","fa-ticket","fa-times","fa-times-circle","fa-times-circle-o","fa-times-rectangle","fa-times-rectangle-o","fa-tint","fa-toggle-down","fa-toggle-left","fa-toggle-off","fa-toggle-on","fa-toggle-right","fa-toggle-up","fa-trademark","fa-train","fa-transgender","fa-transgender-alt","fa-trash","fa-trash-o","fa-tree","fa-trello","fa-tripadvisor","fa-trophy","fa-truck","fa-try","fa-tty","fa-tumblr","fa-tumblr-square","fa-turkish-lira","fa-tv","fa-twitch","fa-twitter","fa-twitter-square","fa-umbrella","fa-underline","fa-undo","fa-universal-access","fa-university","fa-unlink","fa-unlock","fa-unlock-alt","fa-unsorted","fa-upload","fa-usb","fa-usd","fa-user","fa-user-circle","fa-user-circle-o","fa-user-md","fa-user-o","fa-user-plus","fa-user-secret","fa-user-times","fa-users","fa-vcard","fa-vcard-o","fa-venus","fa-venus-double","fa-venus-mars","fa-viacoin","fa-viadeo","fa-viadeo-square","fa-video-camera","fa-vimeo","fa-vimeo-square","fa-vine","fa-vk","fa-volume-control-phone","fa-volume-down","fa-volume-off","fa-volume-up","fa-warning","fa-wechat","fa-weibo","fa-weixin","fa-whatsapp","fa-wheelchair","fa-wheelchair-alt","fa-wifi","fa-wikipedia-w","fa-window-close","fa-window-close-o","fa-window-maximize","fa-window-minimize","fa-window-restore","fa-windows","fa-won","fa-wordpress","fa-wpbeginner","fa-wpexplorer","fa-wpforms","fa-wrench","fa-xing","fa-xing-square","fa-y-combinator","fa-y-combinator-square","fa-yahoo","fa-yc","fa-yc-square","fa-yelp","fa-yen","fa-yoast","fa-youtube","fa-youtube-play","fa-youtube-square"]});;
/*!
 * Chart.js v2.8.0
 * https://www.chartjs.org
 * (c) 2019 Chart.js Contributors
 * Released under the MIT License
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function () { try { return require('moment'); } catch (e) { } }()) :
        typeof define === 'function' && define.amd ? define(['require'], function (require) { return factory(function () { try { return require('moment'); } catch (e) { } }()); }) :
            (global.Chart = factory(global.moment));
}(this, (function (moment) {
    'use strict';

    moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment;

    /* MIT license */

    var conversions = {
        rgb2hsl: rgb2hsl,
        rgb2hsv: rgb2hsv,
        rgb2hwb: rgb2hwb,
        rgb2cmyk: rgb2cmyk,
        rgb2keyword: rgb2keyword,
        rgb2xyz: rgb2xyz,
        rgb2lab: rgb2lab,
        rgb2lch: rgb2lch,

        hsl2rgb: hsl2rgb,
        hsl2hsv: hsl2hsv,
        hsl2hwb: hsl2hwb,
        hsl2cmyk: hsl2cmyk,
        hsl2keyword: hsl2keyword,

        hsv2rgb: hsv2rgb,
        hsv2hsl: hsv2hsl,
        hsv2hwb: hsv2hwb,
        hsv2cmyk: hsv2cmyk,
        hsv2keyword: hsv2keyword,

        hwb2rgb: hwb2rgb,
        hwb2hsl: hwb2hsl,
        hwb2hsv: hwb2hsv,
        hwb2cmyk: hwb2cmyk,
        hwb2keyword: hwb2keyword,

        cmyk2rgb: cmyk2rgb,
        cmyk2hsl: cmyk2hsl,
        cmyk2hsv: cmyk2hsv,
        cmyk2hwb: cmyk2hwb,
        cmyk2keyword: cmyk2keyword,

        keyword2rgb: keyword2rgb,
        keyword2hsl: keyword2hsl,
        keyword2hsv: keyword2hsv,
        keyword2hwb: keyword2hwb,
        keyword2cmyk: keyword2cmyk,
        keyword2lab: keyword2lab,
        keyword2xyz: keyword2xyz,

        xyz2rgb: xyz2rgb,
        xyz2lab: xyz2lab,
        xyz2lch: xyz2lch,

        lab2xyz: lab2xyz,
        lab2rgb: lab2rgb,
        lab2lch: lab2lch,

        lch2lab: lch2lab,
        lch2xyz: lch2xyz,
        lch2rgb: lch2rgb
    };


    function rgb2hsl(rgb) {
        var r = rgb[0] / 255,
            g = rgb[1] / 255,
            b = rgb[2] / 255,
            min = Math.min(r, g, b),
            max = Math.max(r, g, b),
            delta = max - min,
            h, s, l;

        if (max == min)
            h = 0;
        else if (r == max)
            h = (g - b) / delta;
        else if (g == max)
            h = 2 + (b - r) / delta;
        else if (b == max)
            h = 4 + (r - g) / delta;

        h = Math.min(h * 60, 360);

        if (h < 0)
            h += 360;

        l = (min + max) / 2;

        if (max == min)
            s = 0;
        else if (l <= 0.5)
            s = delta / (max + min);
        else
            s = delta / (2 - max - min);

        return [h, s * 100, l * 100];
    }

    function rgb2hsv(rgb) {
        var r = rgb[0],
            g = rgb[1],
            b = rgb[2],
            min = Math.min(r, g, b),
            max = Math.max(r, g, b),
            delta = max - min,
            h, s, v;

        if (max == 0)
            s = 0;
        else
            s = (delta / max * 1000) / 10;

        if (max == min)
            h = 0;
        else if (r == max)
            h = (g - b) / delta;
        else if (g == max)
            h = 2 + (b - r) / delta;
        else if (b == max)
            h = 4 + (r - g) / delta;

        h = Math.min(h * 60, 360);

        if (h < 0)
            h += 360;

        v = ((max / 255) * 1000) / 10;

        return [h, s, v];
    }

    function rgb2hwb(rgb) {
        var r = rgb[0],
            g = rgb[1],
            b = rgb[2],
            h = rgb2hsl(rgb)[0],
            w = 1 / 255 * Math.min(r, Math.min(g, b)),
            b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));

        return [h, w * 100, b * 100];
    }

    function rgb2cmyk(rgb) {
        var r = rgb[0] / 255,
            g = rgb[1] / 255,
            b = rgb[2] / 255,
            c, m, y, k;

        k = Math.min(1 - r, 1 - g, 1 - b);
        c = (1 - r - k) / (1 - k) || 0;
        m = (1 - g - k) / (1 - k) || 0;
        y = (1 - b - k) / (1 - k) || 0;
        return [c * 100, m * 100, y * 100, k * 100];
    }

    function rgb2keyword(rgb) {
        return reverseKeywords[JSON.stringify(rgb)];
    }

    function rgb2xyz(rgb) {
        var r = rgb[0] / 255,
            g = rgb[1] / 255,
            b = rgb[2] / 255;

        // assume sRGB
        r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
        g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
        b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);

        var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
        var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
        var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);

        return [x * 100, y * 100, z * 100];
    }

    function rgb2lab(rgb) {
        var xyz = rgb2xyz(rgb),
            x = xyz[0],
            y = xyz[1],
            z = xyz[2],
            l, a, b;

        x /= 95.047;
        y /= 100;
        z /= 108.883;

        x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
        y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
        z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);

        l = (116 * y) - 16;
        a = 500 * (x - y);
        b = 200 * (y - z);

        return [l, a, b];
    }

    function rgb2lch(args) {
        return lab2lch(rgb2lab(args));
    }

    function hsl2rgb(hsl) {
        var h = hsl[0] / 360,
            s = hsl[1] / 100,
            l = hsl[2] / 100,
            t1, t2, t3, rgb, val;

        if (s == 0) {
            val = l * 255;
            return [val, val, val];
        }

        if (l < 0.5)
            t2 = l * (1 + s);
        else
            t2 = l + s - l * s;
        t1 = 2 * l - t2;

        rgb = [0, 0, 0];
        for (var i = 0; i < 3; i++) {
            t3 = h + 1 / 3 * - (i - 1);
            t3 < 0 && t3++;
            t3 > 1 && t3--;

            if (6 * t3 < 1)
                val = t1 + (t2 - t1) * 6 * t3;
            else if (2 * t3 < 1)
                val = t2;
            else if (3 * t3 < 2)
                val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
            else
                val = t1;

            rgb[i] = val * 255;
        }

        return rgb;
    }

    function hsl2hsv(hsl) {
        var h = hsl[0],
            s = hsl[1] / 100,
            l = hsl[2] / 100,
            sv, v;

        if (l === 0) {
            // no need to do calc on black
            // also avoids divide by 0 error
            return [0, 0, 0];
        }

        l *= 2;
        s *= (l <= 1) ? l : 2 - l;
        v = (l + s) / 2;
        sv = (2 * s) / (l + s);
        return [h, sv * 100, v * 100];
    }

    function hsl2hwb(args) {
        return rgb2hwb(hsl2rgb(args));
    }

    function hsl2cmyk(args) {
        return rgb2cmyk(hsl2rgb(args));
    }

    function hsl2keyword(args) {
        return rgb2keyword(hsl2rgb(args));
    }


    function hsv2rgb(hsv) {
        var h = hsv[0] / 60,
            s = hsv[1] / 100,
            v = hsv[2] / 100,
            hi = Math.floor(h) % 6;

        var f = h - Math.floor(h),
            p = 255 * v * (1 - s),
            q = 255 * v * (1 - (s * f)),
            t = 255 * v * (1 - (s * (1 - f))),
            v = 255 * v;

        switch (hi) {
            case 0:
                return [v, t, p];
            case 1:
                return [q, v, p];
            case 2:
                return [p, v, t];
            case 3:
                return [p, q, v];
            case 4:
                return [t, p, v];
            case 5:
                return [v, p, q];
        }
    }

    function hsv2hsl(hsv) {
        var h = hsv[0],
            s = hsv[1] / 100,
            v = hsv[2] / 100,
            sl, l;

        l = (2 - s) * v;
        sl = s * v;
        sl /= (l <= 1) ? l : 2 - l;
        sl = sl || 0;
        l /= 2;
        return [h, sl * 100, l * 100];
    }

    function hsv2hwb(args) {
        return rgb2hwb(hsv2rgb(args))
    }

    function hsv2cmyk(args) {
        return rgb2cmyk(hsv2rgb(args));
    }

    function hsv2keyword(args) {
        return rgb2keyword(hsv2rgb(args));
    }

    // http://dev.w3.org/csswg/css-color/#hwb-to-rgb
    function hwb2rgb(hwb) {
        var h = hwb[0] / 360,
            wh = hwb[1] / 100,
            bl = hwb[2] / 100,
            ratio = wh + bl,
            i, v, f, n;

        // wh + bl cant be > 1
        if (ratio > 1) {
            wh /= ratio;
            bl /= ratio;
        }

        i = Math.floor(6 * h);
        v = 1 - bl;
        f = 6 * h - i;
        if ((i & 0x01) != 0) {
            f = 1 - f;
        }
        n = wh + f * (v - wh);  // linear interpolation

        switch (i) {
            default:
            case 6:
            case 0: r = v; g = n; b = wh; break;
            case 1: r = n; g = v; b = wh; break;
            case 2: r = wh; g = v; b = n; break;
            case 3: r = wh; g = n; b = v; break;
            case 4: r = n; g = wh; b = v; break;
            case 5: r = v; g = wh; b = n; break;
        }

        return [r * 255, g * 255, b * 255];
    }

    function hwb2hsl(args) {
        return rgb2hsl(hwb2rgb(args));
    }

    function hwb2hsv(args) {
        return rgb2hsv(hwb2rgb(args));
    }

    function hwb2cmyk(args) {
        return rgb2cmyk(hwb2rgb(args));
    }

    function hwb2keyword(args) {
        return rgb2keyword(hwb2rgb(args));
    }

    function cmyk2rgb(cmyk) {
        var c = cmyk[0] / 100,
            m = cmyk[1] / 100,
            y = cmyk[2] / 100,
            k = cmyk[3] / 100,
            r, g, b;

        r = 1 - Math.min(1, c * (1 - k) + k);
        g = 1 - Math.min(1, m * (1 - k) + k);
        b = 1 - Math.min(1, y * (1 - k) + k);
        return [r * 255, g * 255, b * 255];
    }

    function cmyk2hsl(args) {
        return rgb2hsl(cmyk2rgb(args));
    }

    function cmyk2hsv(args) {
        return rgb2hsv(cmyk2rgb(args));
    }

    function cmyk2hwb(args) {
        return rgb2hwb(cmyk2rgb(args));
    }

    function cmyk2keyword(args) {
        return rgb2keyword(cmyk2rgb(args));
    }


    function xyz2rgb(xyz) {
        var x = xyz[0] / 100,
            y = xyz[1] / 100,
            z = xyz[2] / 100,
            r, g, b;

        r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
        g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
        b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);

        // assume sRGB
        r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
            : r = (r * 12.92);

        g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
            : g = (g * 12.92);

        b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
            : b = (b * 12.92);

        r = Math.min(Math.max(0, r), 1);
        g = Math.min(Math.max(0, g), 1);
        b = Math.min(Math.max(0, b), 1);

        return [r * 255, g * 255, b * 255];
    }

    function xyz2lab(xyz) {
        var x = xyz[0],
            y = xyz[1],
            z = xyz[2],
            l, a, b;

        x /= 95.047;
        y /= 100;
        z /= 108.883;

        x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
        y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
        z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);

        l = (116 * y) - 16;
        a = 500 * (x - y);
        b = 200 * (y - z);

        return [l, a, b];
    }

    function xyz2lch(args) {
        return lab2lch(xyz2lab(args));
    }

    function lab2xyz(lab) {
        var l = lab[0],
            a = lab[1],
            b = lab[2],
            x, y, z, y2;

        if (l <= 8) {
            y = (l * 100) / 903.3;
            y2 = (7.787 * (y / 100)) + (16 / 116);
        } else {
            y = 100 * Math.pow((l + 16) / 116, 3);
            y2 = Math.pow(y / 100, 1 / 3);
        }

        x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);

        z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);

        return [x, y, z];
    }

    function lab2lch(lab) {
        var l = lab[0],
            a = lab[1],
            b = lab[2],
            hr, h, c;

        hr = Math.atan2(b, a);
        h = hr * 360 / 2 / Math.PI;
        if (h < 0) {
            h += 360;
        }
        c = Math.sqrt(a * a + b * b);
        return [l, c, h];
    }

    function lab2rgb(args) {
        return xyz2rgb(lab2xyz(args));
    }

    function lch2lab(lch) {
        var l = lch[0],
            c = lch[1],
            h = lch[2],
            a, b, hr;

        hr = h / 360 * 2 * Math.PI;
        a = c * Math.cos(hr);
        b = c * Math.sin(hr);
        return [l, a, b];
    }

    function lch2xyz(args) {
        return lab2xyz(lch2lab(args));
    }

    function lch2rgb(args) {
        return lab2rgb(lch2lab(args));
    }

    function keyword2rgb(keyword) {
        return cssKeywords[keyword];
    }

    function keyword2hsl(args) {
        return rgb2hsl(keyword2rgb(args));
    }

    function keyword2hsv(args) {
        return rgb2hsv(keyword2rgb(args));
    }

    function keyword2hwb(args) {
        return rgb2hwb(keyword2rgb(args));
    }

    function keyword2cmyk(args) {
        return rgb2cmyk(keyword2rgb(args));
    }

    function keyword2lab(args) {
        return rgb2lab(keyword2rgb(args));
    }

    function keyword2xyz(args) {
        return rgb2xyz(keyword2rgb(args));
    }

    var cssKeywords = {
        aliceblue: [240, 248, 255],
        antiquewhite: [250, 235, 215],
        aqua: [0, 255, 255],
        aquamarine: [127, 255, 212],
        azure: [240, 255, 255],
        beige: [245, 245, 220],
        bisque: [255, 228, 196],
        black: [0, 0, 0],
        blanchedalmond: [255, 235, 205],
        blue: [0, 0, 255],
        blueviolet: [138, 43, 226],
        brown: [165, 42, 42],
        burlywood: [222, 184, 135],
        cadetblue: [95, 158, 160],
        chartreuse: [127, 255, 0],
        chocolate: [210, 105, 30],
        coral: [255, 127, 80],
        cornflowerblue: [100, 149, 237],
        cornsilk: [255, 248, 220],
        crimson: [220, 20, 60],
        cyan: [0, 255, 255],
        darkblue: [0, 0, 139],
        darkcyan: [0, 139, 139],
        darkgoldenrod: [184, 134, 11],
        darkgray: [169, 169, 169],
        darkgreen: [0, 100, 0],
        darkgrey: [169, 169, 169],
        darkkhaki: [189, 183, 107],
        darkmagenta: [139, 0, 139],
        darkolivegreen: [85, 107, 47],
        darkorange: [255, 140, 0],
        darkorchid: [153, 50, 204],
        darkred: [139, 0, 0],
        darksalmon: [233, 150, 122],
        darkseagreen: [143, 188, 143],
        darkslateblue: [72, 61, 139],
        darkslategray: [47, 79, 79],
        darkslategrey: [47, 79, 79],
        darkturquoise: [0, 206, 209],
        darkviolet: [148, 0, 211],
        deeppink: [255, 20, 147],
        deepskyblue: [0, 191, 255],
        dimgray: [105, 105, 105],
        dimgrey: [105, 105, 105],
        dodgerblue: [30, 144, 255],
        firebrick: [178, 34, 34],
        floralwhite: [255, 250, 240],
        forestgreen: [34, 139, 34],
        fuchsia: [255, 0, 255],
        gainsboro: [220, 220, 220],
        ghostwhite: [248, 248, 255],
        gold: [255, 215, 0],
        goldenrod: [218, 165, 32],
        gray: [128, 128, 128],
        green: [0, 128, 0],
        greenyellow: [173, 255, 47],
        grey: [128, 128, 128],
        honeydew: [240, 255, 240],
        hotpink: [255, 105, 180],
        indianred: [205, 92, 92],
        indigo: [75, 0, 130],
        ivory: [255, 255, 240],
        khaki: [240, 230, 140],
        lavender: [230, 230, 250],
        lavenderblush: [255, 240, 245],
        lawngreen: [124, 252, 0],
        lemonchiffon: [255, 250, 205],
        lightblue: [173, 216, 230],
        lightcoral: [240, 128, 128],
        lightcyan: [224, 255, 255],
        lightgoldenrodyellow: [250, 250, 210],
        lightgray: [211, 211, 211],
        lightgreen: [144, 238, 144],
        lightgrey: [211, 211, 211],
        lightpink: [255, 182, 193],
        lightsalmon: [255, 160, 122],
        lightseagreen: [32, 178, 170],
        lightskyblue: [135, 206, 250],
        lightslategray: [119, 136, 153],
        lightslategrey: [119, 136, 153],
        lightsteelblue: [176, 196, 222],
        lightyellow: [255, 255, 224],
        lime: [0, 255, 0],
        limegreen: [50, 205, 50],
        linen: [250, 240, 230],
        magenta: [255, 0, 255],
        maroon: [128, 0, 0],
        mediumaquamarine: [102, 205, 170],
        mediumblue: [0, 0, 205],
        mediumorchid: [186, 85, 211],
        mediumpurple: [147, 112, 219],
        mediumseagreen: [60, 179, 113],
        mediumslateblue: [123, 104, 238],
        mediumspringgreen: [0, 250, 154],
        mediumturquoise: [72, 209, 204],
        mediumvioletred: [199, 21, 133],
        midnightblue: [25, 25, 112],
        mintcream: [245, 255, 250],
        mistyrose: [255, 228, 225],
        moccasin: [255, 228, 181],
        navajowhite: [255, 222, 173],
        navy: [0, 0, 128],
        oldlace: [253, 245, 230],
        olive: [128, 128, 0],
        olivedrab: [107, 142, 35],
        orange: [255, 165, 0],
        orangered: [255, 69, 0],
        orchid: [218, 112, 214],
        palegoldenrod: [238, 232, 170],
        palegreen: [152, 251, 152],
        paleturquoise: [175, 238, 238],
        palevioletred: [219, 112, 147],
        papayawhip: [255, 239, 213],
        peachpuff: [255, 218, 185],
        peru: [205, 133, 63],
        pink: [255, 192, 203],
        plum: [221, 160, 221],
        powderblue: [176, 224, 230],
        purple: [128, 0, 128],
        rebeccapurple: [102, 51, 153],
        red: [255, 0, 0],
        rosybrown: [188, 143, 143],
        royalblue: [65, 105, 225],
        saddlebrown: [139, 69, 19],
        salmon: [250, 128, 114],
        sandybrown: [244, 164, 96],
        seagreen: [46, 139, 87],
        seashell: [255, 245, 238],
        sienna: [160, 82, 45],
        silver: [192, 192, 192],
        skyblue: [135, 206, 235],
        slateblue: [106, 90, 205],
        slategray: [112, 128, 144],
        slategrey: [112, 128, 144],
        snow: [255, 250, 250],
        springgreen: [0, 255, 127],
        steelblue: [70, 130, 180],
        tan: [210, 180, 140],
        teal: [0, 128, 128],
        thistle: [216, 191, 216],
        tomato: [255, 99, 71],
        turquoise: [64, 224, 208],
        violet: [238, 130, 238],
        wheat: [245, 222, 179],
        white: [255, 255, 255],
        whitesmoke: [245, 245, 245],
        yellow: [255, 255, 0],
        yellowgreen: [154, 205, 50]
    };

    var reverseKeywords = {};
    for (var key in cssKeywords) {
        reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
    }

    var convert = function () {
        return new Converter();
    };

    for (var func in conversions) {
        // export Raw versions
        convert[func + "Raw"] = (function (func) {
            // accept array or plain args
            return function (arg) {
                if (typeof arg == "number")
                    arg = Array.prototype.slice.call(arguments);
                return conversions[func](arg);
            }
        })(func);

        var pair = /(\w+)2(\w+)/.exec(func),
            from = pair[1],
            to = pair[2];

        // export rgb2hsl and ["rgb"]["hsl"]
        convert[from] = convert[from] || {};

        convert[from][to] = convert[func] = (function (func) {
            return function (arg) {
                if (typeof arg == "number")
                    arg = Array.prototype.slice.call(arguments);

                var val = conversions[func](arg);
                if (typeof val == "string" || val === undefined)
                    return val; // keyword

                for (var i = 0; i < val.length; i++)
                    val[i] = Math.round(val[i]);
                return val;
            }
        })(func);
    }


    /* Converter does lazy conversion and caching */
    var Converter = function () {
        this.convs = {};
    };

    /* Either get the values for a space or
      set the values for a space, depending on args */
    Converter.prototype.routeSpace = function (space, args) {
        var values = args[0];
        if (values === undefined) {
            // color.rgb()
            return this.getValues(space);
        }
        // color.rgb(10, 10, 10)
        if (typeof values == "number") {
            values = Array.prototype.slice.call(args);
        }

        return this.setValues(space, values);
    };

    /* Set the values for a space, invalidating cache */
    Converter.prototype.setValues = function (space, values) {
        this.space = space;
        this.convs = {};
        this.convs[space] = values;
        return this;
    };

    /* Get the values for a space. If there's already
      a conversion for the space, fetch it, otherwise
      compute it */
    Converter.prototype.getValues = function (space) {
        var vals = this.convs[space];
        if (!vals) {
            var fspace = this.space,
                from = this.convs[fspace];
            vals = convert[fspace][space](from);

            this.convs[space] = vals;
        }
        return vals;
    };

    ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function (space) {
        Converter.prototype[space] = function (vals) {
            return this.routeSpace(space, arguments);
        };
    });

    var colorConvert = convert;

    var colorName = {
        "aliceblue": [240, 248, 255],
        "antiquewhite": [250, 235, 215],
        "aqua": [0, 255, 255],
        "aquamarine": [127, 255, 212],
        "azure": [240, 255, 255],
        "beige": [245, 245, 220],
        "bisque": [255, 228, 196],
        "black": [0, 0, 0],
        "blanchedalmond": [255, 235, 205],
        "blue": [0, 0, 255],
        "blueviolet": [138, 43, 226],
        "brown": [165, 42, 42],
        "burlywood": [222, 184, 135],
        "cadetblue": [95, 158, 160],
        "chartreuse": [127, 255, 0],
        "chocolate": [210, 105, 30],
        "coral": [255, 127, 80],
        "cornflowerblue": [100, 149, 237],
        "cornsilk": [255, 248, 220],
        "crimson": [220, 20, 60],
        "cyan": [0, 255, 255],
        "darkblue": [0, 0, 139],
        "darkcyan": [0, 139, 139],
        "darkgoldenrod": [184, 134, 11],
        "darkgray": [169, 169, 169],
        "darkgreen": [0, 100, 0],
        "darkgrey": [169, 169, 169],
        "darkkhaki": [189, 183, 107],
        "darkmagenta": [139, 0, 139],
        "darkolivegreen": [85, 107, 47],
        "darkorange": [255, 140, 0],
        "darkorchid": [153, 50, 204],
        "darkred": [139, 0, 0],
        "darksalmon": [233, 150, 122],
        "darkseagreen": [143, 188, 143],
        "darkslateblue": [72, 61, 139],
        "darkslategray": [47, 79, 79],
        "darkslategrey": [47, 79, 79],
        "darkturquoise": [0, 206, 209],
        "darkviolet": [148, 0, 211],
        "deeppink": [255, 20, 147],
        "deepskyblue": [0, 191, 255],
        "dimgray": [105, 105, 105],
        "dimgrey": [105, 105, 105],
        "dodgerblue": [30, 144, 255],
        "firebrick": [178, 34, 34],
        "floralwhite": [255, 250, 240],
        "forestgreen": [34, 139, 34],
        "fuchsia": [255, 0, 255],
        "gainsboro": [220, 220, 220],
        "ghostwhite": [248, 248, 255],
        "gold": [255, 215, 0],
        "goldenrod": [218, 165, 32],
        "gray": [128, 128, 128],
        "green": [0, 128, 0],
        "greenyellow": [173, 255, 47],
        "grey": [128, 128, 128],
        "honeydew": [240, 255, 240],
        "hotpink": [255, 105, 180],
        "indianred": [205, 92, 92],
        "indigo": [75, 0, 130],
        "ivory": [255, 255, 240],
        "khaki": [240, 230, 140],
        "lavender": [230, 230, 250],
        "lavenderblush": [255, 240, 245],
        "lawngreen": [124, 252, 0],
        "lemonchiffon": [255, 250, 205],
        "lightblue": [173, 216, 230],
        "lightcoral": [240, 128, 128],
        "lightcyan": [224, 255, 255],
        "lightgoldenrodyellow": [250, 250, 210],
        "lightgray": [211, 211, 211],
        "lightgreen": [144, 238, 144],
        "lightgrey": [211, 211, 211],
        "lightpink": [255, 182, 193],
        "lightsalmon": [255, 160, 122],
        "lightseagreen": [32, 178, 170],
        "lightskyblue": [135, 206, 250],
        "lightslategray": [119, 136, 153],
        "lightslategrey": [119, 136, 153],
        "lightsteelblue": [176, 196, 222],
        "lightyellow": [255, 255, 224],
        "lime": [0, 255, 0],
        "limegreen": [50, 205, 50],
        "linen": [250, 240, 230],
        "magenta": [255, 0, 255],
        "maroon": [128, 0, 0],
        "mediumaquamarine": [102, 205, 170],
        "mediumblue": [0, 0, 205],
        "mediumorchid": [186, 85, 211],
        "mediumpurple": [147, 112, 219],
        "mediumseagreen": [60, 179, 113],
        "mediumslateblue": [123, 104, 238],
        "mediumspringgreen": [0, 250, 154],
        "mediumturquoise": [72, 209, 204],
        "mediumvioletred": [199, 21, 133],
        "midnightblue": [25, 25, 112],
        "mintcream": [245, 255, 250],
        "mistyrose": [255, 228, 225],
        "moccasin": [255, 228, 181],
        "navajowhite": [255, 222, 173],
        "navy": [0, 0, 128],
        "oldlace": [253, 245, 230],
        "olive": [128, 128, 0],
        "olivedrab": [107, 142, 35],
        "orange": [255, 165, 0],
        "orangered": [255, 69, 0],
        "orchid": [218, 112, 214],
        "palegoldenrod": [238, 232, 170],
        "palegreen": [152, 251, 152],
        "paleturquoise": [175, 238, 238],
        "palevioletred": [219, 112, 147],
        "papayawhip": [255, 239, 213],
        "peachpuff": [255, 218, 185],
        "peru": [205, 133, 63],
        "pink": [255, 192, 203],
        "plum": [221, 160, 221],
        "powderblue": [176, 224, 230],
        "purple": [128, 0, 128],
        "rebeccapurple": [102, 51, 153],
        "red": [255, 0, 0],
        "rosybrown": [188, 143, 143],
        "royalblue": [65, 105, 225],
        "saddlebrown": [139, 69, 19],
        "salmon": [250, 128, 114],
        "sandybrown": [244, 164, 96],
        "seagreen": [46, 139, 87],
        "seashell": [255, 245, 238],
        "sienna": [160, 82, 45],
        "silver": [192, 192, 192],
        "skyblue": [135, 206, 235],
        "slateblue": [106, 90, 205],
        "slategray": [112, 128, 144],
        "slategrey": [112, 128, 144],
        "snow": [255, 250, 250],
        "springgreen": [0, 255, 127],
        "steelblue": [70, 130, 180],
        "tan": [210, 180, 140],
        "teal": [0, 128, 128],
        "thistle": [216, 191, 216],
        "tomato": [255, 99, 71],
        "turquoise": [64, 224, 208],
        "violet": [238, 130, 238],
        "wheat": [245, 222, 179],
        "white": [255, 255, 255],
        "whitesmoke": [245, 245, 245],
        "yellow": [255, 255, 0],
        "yellowgreen": [154, 205, 50]
    };

    /* MIT license */


    var colorString = {
        getRgba: getRgba,
        getHsla: getHsla,
        getRgb: getRgb,
        getHsl: getHsl,
        getHwb: getHwb,
        getAlpha: getAlpha,

        hexString: hexString,
        rgbString: rgbString,
        rgbaString: rgbaString,
        percentString: percentString,
        percentaString: percentaString,
        hslString: hslString,
        hslaString: hslaString,
        hwbString: hwbString,
        keyword: keyword
    };

    function getRgba(string) {
        if (!string) {
            return;
        }
        var abbr = /^#([a-fA-F0-9]{3,4})$/i,
            hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i,
            rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
            per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
            keyword = /(\w+)/;

        var rgb = [0, 0, 0],
            a = 1,
            match = string.match(abbr),
            hexAlpha = "";
        if (match) {
            match = match[1];
            hexAlpha = match[3];
            for (var i = 0; i < rgb.length; i++) {
                rgb[i] = parseInt(match[i] + match[i], 16);
            }
            if (hexAlpha) {
                a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
            }
        }
        else if (match = string.match(hex)) {
            hexAlpha = match[2];
            match = match[1];
            for (var i = 0; i < rgb.length; i++) {
                rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
            }
            if (hexAlpha) {
                a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
            }
        }
        else if (match = string.match(rgba)) {
            for (var i = 0; i < rgb.length; i++) {
                rgb[i] = parseInt(match[i + 1]);
            }
            a = parseFloat(match[4]);
        }
        else if (match = string.match(per)) {
            for (var i = 0; i < rgb.length; i++) {
                rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
            }
            a = parseFloat(match[4]);
        }
        else if (match = string.match(keyword)) {
            if (match[1] == "transparent") {
                return [0, 0, 0, 0];
            }
            rgb = colorName[match[1]];
            if (!rgb) {
                return;
            }
        }

        for (var i = 0; i < rgb.length; i++) {
            rgb[i] = scale(rgb[i], 0, 255);
        }
        if (!a && a != 0) {
            a = 1;
        }
        else {
            a = scale(a, 0, 1);
        }
        rgb[3] = a;
        return rgb;
    }

    function getHsla(string) {
        if (!string) {
            return;
        }
        var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
        var match = string.match(hsl);
        if (match) {
            var alpha = parseFloat(match[4]);
            var h = scale(parseInt(match[1]), 0, 360),
                s = scale(parseFloat(match[2]), 0, 100),
                l = scale(parseFloat(match[3]), 0, 100),
                a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
            return [h, s, l, a];
        }
    }

    function getHwb(string) {
        if (!string) {
            return;
        }
        var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
        var match = string.match(hwb);
        if (match) {
            var alpha = parseFloat(match[4]);
            var h = scale(parseInt(match[1]), 0, 360),
                w = scale(parseFloat(match[2]), 0, 100),
                b = scale(parseFloat(match[3]), 0, 100),
                a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
            return [h, w, b, a];
        }
    }

    function getRgb(string) {
        var rgba = getRgba(string);
        return rgba && rgba.slice(0, 3);
    }

    function getHsl(string) {
        var hsla = getHsla(string);
        return hsla && hsla.slice(0, 3);
    }

    function getAlpha(string) {
        var vals = getRgba(string);
        if (vals) {
            return vals[3];
        }
        else if (vals = getHsla(string)) {
            return vals[3];
        }
        else if (vals = getHwb(string)) {
            return vals[3];
        }
    }

    // generators
    function hexString(rgba, a) {
        var a = (a !== undefined && rgba.length === 3) ? a : rgba[3];
        return "#" + hexDouble(rgba[0])
            + hexDouble(rgba[1])
            + hexDouble(rgba[2])
            + (
                (a >= 0 && a < 1)
                    ? hexDouble(Math.round(a * 255))
                    : ""
            );
    }

    function rgbString(rgba, alpha) {
        if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
            return rgbaString(rgba, alpha);
        }
        return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
    }

    function rgbaString(rgba, alpha) {
        if (alpha === undefined) {
            alpha = (rgba[3] !== undefined ? rgba[3] : 1);
        }
        return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
            + ", " + alpha + ")";
    }

    function percentString(rgba, alpha) {
        if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
            return percentaString(rgba, alpha);
        }
        var r = Math.round(rgba[0] / 255 * 100),
            g = Math.round(rgba[1] / 255 * 100),
            b = Math.round(rgba[2] / 255 * 100);

        return "rgb(" + r + "%, " + g + "%, " + b + "%)";
    }

    function percentaString(rgba, alpha) {
        var r = Math.round(rgba[0] / 255 * 100),
            g = Math.round(rgba[1] / 255 * 100),
            b = Math.round(rgba[2] / 255 * 100);
        return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
    }

    function hslString(hsla, alpha) {
        if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
            return hslaString(hsla, alpha);
        }
        return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
    }

    function hslaString(hsla, alpha) {
        if (alpha === undefined) {
            alpha = (hsla[3] !== undefined ? hsla[3] : 1);
        }
        return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
            + alpha + ")";
    }

    // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
    // (hwb have alpha optional & 1 is default value)
    function hwbString(hwb, alpha) {
        if (alpha === undefined) {
            alpha = (hwb[3] !== undefined ? hwb[3] : 1);
        }
        return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
            + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
    }

    function keyword(rgb) {
        return reverseNames[rgb.slice(0, 3)];
    }

    // helpers
    function scale(num, min, max) {
        return Math.min(Math.max(min, num), max);
    }

    function hexDouble(num) {
        var str = num.toString(16).toUpperCase();
        return (str.length < 2) ? "0" + str : str;
    }


    //create a list of reverse color names
    var reverseNames = {};
    for (var name in colorName) {
        reverseNames[colorName[name]] = name;
    }

    /* MIT license */



    var Color = function (obj) {
        if (obj instanceof Color) {
            return obj;
        }
        if (!(this instanceof Color)) {
            return new Color(obj);
        }

        this.valid = false;
        this.values = {
            rgb: [0, 0, 0],
            hsl: [0, 0, 0],
            hsv: [0, 0, 0],
            hwb: [0, 0, 0],
            cmyk: [0, 0, 0, 0],
            alpha: 1
        };

        // parse Color() argument
        var vals;
        if (typeof obj === 'string') {
            vals = colorString.getRgba(obj);
            if (vals) {
                this.setValues('rgb', vals);
            } else if (vals = colorString.getHsla(obj)) {
                this.setValues('hsl', vals);
            } else if (vals = colorString.getHwb(obj)) {
                this.setValues('hwb', vals);
            }
        } else if (typeof obj === 'object') {
            vals = obj;
            if (vals.r !== undefined || vals.red !== undefined) {
                this.setValues('rgb', vals);
            } else if (vals.l !== undefined || vals.lightness !== undefined) {
                this.setValues('hsl', vals);
            } else if (vals.v !== undefined || vals.value !== undefined) {
                this.setValues('hsv', vals);
            } else if (vals.w !== undefined || vals.whiteness !== undefined) {
                this.setValues('hwb', vals);
            } else if (vals.c !== undefined || vals.cyan !== undefined) {
                this.setValues('cmyk', vals);
            }
        }
    };

    Color.prototype = {
        isValid: function () {
            return this.valid;
        },
        rgb: function () {
            return this.setSpace('rgb', arguments);
        },
        hsl: function () {
            return this.setSpace('hsl', arguments);
        },
        hsv: function () {
            return this.setSpace('hsv', arguments);
        },
        hwb: function () {
            return this.setSpace('hwb', arguments);
        },
        cmyk: function () {
            return this.setSpace('cmyk', arguments);
        },

        rgbArray: function () {
            return this.values.rgb;
        },
        hslArray: function () {
            return this.values.hsl;
        },
        hsvArray: function () {
            return this.values.hsv;
        },
        hwbArray: function () {
            var values = this.values;
            if (values.alpha !== 1) {
                return values.hwb.concat([values.alpha]);
            }
            return values.hwb;
        },
        cmykArray: function () {
            return this.values.cmyk;
        },
        rgbaArray: function () {
            var values = this.values;
            return values.rgb.concat([values.alpha]);
        },
        hslaArray: function () {
            var values = this.values;
            return values.hsl.concat([values.alpha]);
        },
        alpha: function (val) {
            if (val === undefined) {
                return this.values.alpha;
            }
            this.setValues('alpha', val);
            return this;
        },

        red: function (val) {
            return this.setChannel('rgb', 0, val);
        },
        green: function (val) {
            return this.setChannel('rgb', 1, val);
        },
        blue: function (val) {
            return this.setChannel('rgb', 2, val);
        },
        hue: function (val) {
            if (val) {
                val %= 360;
                val = val < 0 ? 360 + val : val;
            }
            return this.setChannel('hsl', 0, val);
        },
        saturation: function (val) {
            return this.setChannel('hsl', 1, val);
        },
        lightness: function (val) {
            return this.setChannel('hsl', 2, val);
        },
        saturationv: function (val) {
            return this.setChannel('hsv', 1, val);
        },
        whiteness: function (val) {
            return this.setChannel('hwb', 1, val);
        },
        blackness: function (val) {
            return this.setChannel('hwb', 2, val);
        },
        value: function (val) {
            return this.setChannel('hsv', 2, val);
        },
        cyan: function (val) {
            return this.setChannel('cmyk', 0, val);
        },
        magenta: function (val) {
            return this.setChannel('cmyk', 1, val);
        },
        yellow: function (val) {
            return this.setChannel('cmyk', 2, val);
        },
        black: function (val) {
            return this.setChannel('cmyk', 3, val);
        },

        hexString: function () {
            return colorString.hexString(this.values.rgb);
        },
        rgbString: function () {
            return colorString.rgbString(this.values.rgb, this.values.alpha);
        },
        rgbaString: function () {
            return colorString.rgbaString(this.values.rgb, this.values.alpha);
        },
        percentString: function () {
            return colorString.percentString(this.values.rgb, this.values.alpha);
        },
        hslString: function () {
            return colorString.hslString(this.values.hsl, this.values.alpha);
        },
        hslaString: function () {
            return colorString.hslaString(this.values.hsl, this.values.alpha);
        },
        hwbString: function () {
            return colorString.hwbString(this.values.hwb, this.values.alpha);
        },
        keyword: function () {
            return colorString.keyword(this.values.rgb, this.values.alpha);
        },

        rgbNumber: function () {
            var rgb = this.values.rgb;
            return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
        },

        luminosity: function () {
            // http://www.w3.org/TR/WCAG20/#relativeluminancedef
            var rgb = this.values.rgb;
            var lum = [];
            for (var i = 0; i < rgb.length; i++) {
                var chan = rgb[i] / 255;
                lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
            }
            return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
        },

        contrast: function (color2) {
            // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
            var lum1 = this.luminosity();
            var lum2 = color2.luminosity();
            if (lum1 > lum2) {
                return (lum1 + 0.05) / (lum2 + 0.05);
            }
            return (lum2 + 0.05) / (lum1 + 0.05);
        },

        level: function (color2) {
            var contrastRatio = this.contrast(color2);
            if (contrastRatio >= 7.1) {
                return 'AAA';
            }

            return (contrastRatio >= 4.5) ? 'AA' : '';
        },

        dark: function () {
            // YIQ equation from http://24ways.org/2010/calculating-color-contrast
            var rgb = this.values.rgb;
            var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
            return yiq < 128;
        },

        light: function () {
            return !this.dark();
        },

        negate: function () {
            var rgb = [];
            for (var i = 0; i < 3; i++) {
                rgb[i] = 255 - this.values.rgb[i];
            }
            this.setValues('rgb', rgb);
            return this;
        },

        lighten: function (ratio) {
            var hsl = this.values.hsl;
            hsl[2] += hsl[2] * ratio;
            this.setValues('hsl', hsl);
            return this;
        },

        darken: function (ratio) {
            var hsl = this.values.hsl;
            hsl[2] -= hsl[2] * ratio;
            this.setValues('hsl', hsl);
            return this;
        },

        saturate: function (ratio) {
            var hsl = this.values.hsl;
            hsl[1] += hsl[1] * ratio;
            this.setValues('hsl', hsl);
            return this;
        },

        desaturate: function (ratio) {
            var hsl = this.values.hsl;
            hsl[1] -= hsl[1] * ratio;
            this.setValues('hsl', hsl);
            return this;
        },

        whiten: function (ratio) {
            var hwb = this.values.hwb;
            hwb[1] += hwb[1] * ratio;
            this.setValues('hwb', hwb);
            return this;
        },

        blacken: function (ratio) {
            var hwb = this.values.hwb;
            hwb[2] += hwb[2] * ratio;
            this.setValues('hwb', hwb);
            return this;
        },

        greyscale: function () {
            var rgb = this.values.rgb;
            // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
            var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
            this.setValues('rgb', [val, val, val]);
            return this;
        },

        clearer: function (ratio) {
            var alpha = this.values.alpha;
            this.setValues('alpha', alpha - (alpha * ratio));
            return this;
        },

        opaquer: function (ratio) {
            var alpha = this.values.alpha;
            this.setValues('alpha', alpha + (alpha * ratio));
            return this;
        },

        rotate: function (degrees) {
            var hsl = this.values.hsl;
            var hue = (hsl[0] + degrees) % 360;
            hsl[0] = hue < 0 ? 360 + hue : hue;
            this.setValues('hsl', hsl);
            return this;
        },

        /**
         * Ported from sass implementation in C
         * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
         */
        mix: function (mixinColor, weight) {
            var color1 = this;
            var color2 = mixinColor;
            var p = weight === undefined ? 0.5 : weight;

            var w = 2 * p - 1;
            var a = color1.alpha() - color2.alpha();

            var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
            var w2 = 1 - w1;

            return this
                .rgb(
                    w1 * color1.red() + w2 * color2.red(),
                    w1 * color1.green() + w2 * color2.green(),
                    w1 * color1.blue() + w2 * color2.blue()
                )
                .alpha(color1.alpha() * p + color2.alpha() * (1 - p));
        },

        toJSON: function () {
            return this.rgb();
        },

        clone: function () {
            // NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
            // making the final build way to big to embed in Chart.js. So let's do it manually,
            // assuming that values to clone are 1 dimension arrays containing only numbers,
            // except 'alpha' which is a number.
            var result = new Color();
            var source = this.values;
            var target = result.values;
            var value, type;

            for (var prop in source) {
                if (source.hasOwnProperty(prop)) {
                    value = source[prop];
                    type = ({}).toString.call(value);
                    if (type === '[object Array]') {
                        target[prop] = value.slice(0);
                    } else if (type === '[object Number]') {
                        target[prop] = value;
                    } else {
                        console.error('unexpected color value:', value);
                    }
                }
            }

            return result;
        }
    };

    Color.prototype.spaces = {
        rgb: ['red', 'green', 'blue'],
        hsl: ['hue', 'saturation', 'lightness'],
        hsv: ['hue', 'saturation', 'value'],
        hwb: ['hue', 'whiteness', 'blackness'],
        cmyk: ['cyan', 'magenta', 'yellow', 'black']
    };

    Color.prototype.maxes = {
        rgb: [255, 255, 255],
        hsl: [360, 100, 100],
        hsv: [360, 100, 100],
        hwb: [360, 100, 100],
        cmyk: [100, 100, 100, 100]
    };

    Color.prototype.getValues = function (space) {
        var values = this.values;
        var vals = {};

        for (var i = 0; i < space.length; i++) {
            vals[space.charAt(i)] = values[space][i];
        }

        if (values.alpha !== 1) {
            vals.a = values.alpha;
        }

        // {r: 255, g: 255, b: 255, a: 0.4}
        return vals;
    };

    Color.prototype.setValues = function (space, vals) {
        var values = this.values;
        var spaces = this.spaces;
        var maxes = this.maxes;
        var alpha = 1;
        var i;

        this.valid = true;

        if (space === 'alpha') {
            alpha = vals;
        } else if (vals.length) {
            // [10, 10, 10]
            values[space] = vals.slice(0, space.length);
            alpha = vals[space.length];
        } else if (vals[space.charAt(0)] !== undefined) {
            // {r: 10, g: 10, b: 10}
            for (i = 0; i < space.length; i++) {
                values[space][i] = vals[space.charAt(i)];
            }

            alpha = vals.a;
        } else if (vals[spaces[space][0]] !== undefined) {
            // {red: 10, green: 10, blue: 10}
            var chans = spaces[space];

            for (i = 0; i < space.length; i++) {
                values[space][i] = vals[chans[i]];
            }

            alpha = vals.alpha;
        }

        values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));

        if (space === 'alpha') {
            return false;
        }

        var capped;

        // cap values of the space prior converting all values
        for (i = 0; i < space.length; i++) {
            capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
            values[space][i] = Math.round(capped);
        }

        // convert to all the other color spaces
        for (var sname in spaces) {
            if (sname !== space) {
                values[sname] = colorConvert[space][sname](values[space]);
            }
        }

        return true;
    };

    Color.prototype.setSpace = function (space, args) {
        var vals = args[0];

        if (vals === undefined) {
            // color.rgb()
            return this.getValues(space);
        }

        // color.rgb(10, 10, 10)
        if (typeof vals === 'number') {
            vals = Array.prototype.slice.call(args);
        }

        this.setValues(space, vals);
        return this;
    };

    Color.prototype.setChannel = function (space, index, val) {
        var svalues = this.values[space];
        if (val === undefined) {
            // color.red()
            return svalues[index];
        } else if (val === svalues[index]) {
            // color.red(color.red())
            return this;
        }

        // color.red(100)
        svalues[index] = val;
        this.setValues(space, svalues);

        return this;
    };

    if (typeof window !== 'undefined') {
        window.Color = Color;
    }

    var chartjsColor = Color;

    /**
     * @namespace Chart.helpers
     */
    var helpers = {
        /**
         * An empty function that can be used, for example, for optional callback.
         */
        noop: function () { },

        /**
         * Returns a unique id, sequentially generated from a global variable.
         * @returns {number}
         * @function
         */
        uid: (function () {
            var id = 0;
            return function () {
                return id++;
            };
        }()),

        /**
         * Returns true if `value` is neither null nor undefined, else returns false.
         * @param {*} value - The value to test.
         * @returns {boolean}
         * @since 2.7.0
         */
        isNullOrUndef: function (value) {
            return value === null || typeof value === 'undefined';
        },

        /**
         * Returns true if `value` is an array (including typed arrays), else returns false.
         * @param {*} value - The value to test.
         * @returns {boolean}
         * @function
         */
        isArray: function (value) {
            if (Array.isArray && Array.isArray(value)) {
                return true;
            }
            var type = Object.prototype.toString.call(value);
            if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
                return true;
            }
            return false;
        },

        /**
         * Returns true if `value` is an object (excluding null), else returns false.
         * @param {*} value - The value to test.
         * @returns {boolean}
         * @since 2.7.0
         */
        isObject: function (value) {
            return value !== null && Object.prototype.toString.call(value) === '[object Object]';
        },

        /**
         * Returns true if `value` is a finite number, else returns false
         * @param {*} value  - The value to test.
         * @returns {boolean}
         */
        isFinite: function (value) {
            return (typeof value === 'number' || value instanceof Number) && isFinite(value);
        },

        /**
         * Returns `value` if defined, else returns `defaultValue`.
         * @param {*} value - The value to return if defined.
         * @param {*} defaultValue - The value to return if `value` is undefined.
         * @returns {*}
         */
        valueOrDefault: function (value, defaultValue) {
            return typeof value === 'undefined' ? defaultValue : value;
        },

        /**
         * Returns value at the given `index` in array if defined, else returns `defaultValue`.
         * @param {Array} value - The array to lookup for value at `index`.
         * @param {number} index - The index in `value` to lookup for value.
         * @param {*} defaultValue - The value to return if `value[index]` is undefined.
         * @returns {*}
         */
        valueAtIndexOrDefault: function (value, index, defaultValue) {
            return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
        },

        /**
         * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
         * value returned by `fn`. If `fn` is not a function, this method returns undefined.
         * @param {function} fn - The function to call.
         * @param {Array|undefined|null} args - The arguments with which `fn` should be called.
         * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
         * @returns {*}
         */
        callback: function (fn, args, thisArg) {
            if (fn && typeof fn.call === 'function') {
                return fn.apply(thisArg, args);
            }
        },

        /**
         * Note(SB) for performance sake, this method should only be used when loopable type
         * is unknown or in none intensive code (not called often and small loopable). Else
         * it's preferable to use a regular for() loop and save extra function calls.
         * @param {object|Array} loopable - The object or array to be iterated.
         * @param {function} fn - The function to call for each item.
         * @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
         * @param {boolean} [reverse] - If true, iterates backward on the loopable.
         */
        each: function (loopable, fn, thisArg, reverse) {
            var i, len, keys;
            if (helpers.isArray(loopable)) {
                len = loopable.length;
                if (reverse) {
                    for (i = len - 1; i >= 0; i--) {
                        fn.call(thisArg, loopable[i], i);
                    }
                } else {
                    for (i = 0; i < len; i++) {
                        fn.call(thisArg, loopable[i], i);
                    }
                }
            } else if (helpers.isObject(loopable)) {
                keys = Object.keys(loopable);
                len = keys.length;
                for (i = 0; i < len; i++) {
                    fn.call(thisArg, loopable[keys[i]], keys[i]);
                }
            }
        },

        /**
         * Returns true if the `a0` and `a1` arrays have the same content, else returns false.
         * @see https://stackoverflow.com/a/14853974
         * @param {Array} a0 - The array to compare
         * @param {Array} a1 - The array to compare
         * @returns {boolean}
         */
        arrayEquals: function (a0, a1) {
            var i, ilen, v0, v1;

            if (!a0 || !a1 || a0.length !== a1.length) {
                return false;
            }

            for (i = 0, ilen = a0.length; i < ilen; ++i) {
                v0 = a0[i];
                v1 = a1[i];

                if (v0 instanceof Array && v1 instanceof Array) {
                    if (!helpers.arrayEquals(v0, v1)) {
                        return false;
                    }
                } else if (v0 !== v1) {
                    // NOTE: two different object instances will never be equal: {x:20} != {x:20}
                    return false;
                }
            }

            return true;
        },

        /**
         * Returns a deep copy of `source` without keeping references on objects and arrays.
         * @param {*} source - The value to clone.
         * @returns {*}
         */
        clone: function (source) {
            if (helpers.isArray(source)) {
                return source.map(helpers.clone);
            }

            if (helpers.isObject(source)) {
                var target = {};
                var keys = Object.keys(source);
                var klen = keys.length;
                var k = 0;

                for (; k < klen; ++k) {
                    target[keys[k]] = helpers.clone(source[keys[k]]);
                }

                return target;
            }

            return source;
        },

        /**
         * The default merger when Chart.helpers.merge is called without merger option.
         * Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
         * @private
         */
        _merger: function (key, target, source, options) {
            var tval = target[key];
            var sval = source[key];

            if (helpers.isObject(tval) && helpers.isObject(sval)) {
                helpers.merge(tval, sval, options);
            } else {
                target[key] = helpers.clone(sval);
            }
        },

        /**
         * Merges source[key] in target[key] only if target[key] is undefined.
         * @private
         */
        _mergerIf: function (key, target, source) {
            var tval = target[key];
            var sval = source[key];

            if (helpers.isObject(tval) && helpers.isObject(sval)) {
                helpers.mergeIf(tval, sval);
            } else if (!target.hasOwnProperty(key)) {
                target[key] = helpers.clone(sval);
            }
        },

        /**
         * Recursively deep copies `source` properties into `target` with the given `options`.
         * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
         * @param {object} target - The target object in which all sources are merged into.
         * @param {object|object[]} source - Object(s) to merge into `target`.
         * @param {object} [options] - Merging options:
         * @param {function} [options.merger] - The merge method (key, target, source, options)
         * @returns {object} The `target` object.
         */
        merge: function (target, source, options) {
            var sources = helpers.isArray(source) ? source : [source];
            var ilen = sources.length;
            var merge, i, keys, klen, k;

            if (!helpers.isObject(target)) {
                return target;
            }

            options = options || {};
            merge = options.merger || helpers._merger;

            for (i = 0; i < ilen; ++i) {
                source = sources[i];
                if (!helpers.isObject(source)) {
                    continue;
                }

                keys = Object.keys(source);
                for (k = 0, klen = keys.length; k < klen; ++k) {
                    merge(keys[k], target, source, options);
                }
            }

            return target;
        },

        /**
         * Recursively deep copies `source` properties into `target` *only* if not defined in target.
         * IMPORTANT: `target` is not cloned and will be updated with `source` properties.
         * @param {object} target - The target object in which all sources are merged into.
         * @param {object|object[]} source - Object(s) to merge into `target`.
         * @returns {object} The `target` object.
         */
        mergeIf: function (target, source) {
            return helpers.merge(target, source, { merger: helpers._mergerIf });
        },

        /**
         * Applies the contents of two or more objects together into the first object.
         * @param {object} target - The target object in which all objects are merged into.
         * @param {object} arg1 - Object containing additional properties to merge in target.
         * @param {object} argN - Additional objects containing properties to merge in target.
         * @returns {object} The `target` object.
         */
        extend: function (target) {
            var setFn = function (value, key) {
                target[key] = value;
            };
            for (var i = 1, ilen = arguments.length; i < ilen; ++i) {
                helpers.each(arguments[i], setFn);
            }
            return target;
        },

        /**
         * Basic javascript inheritance based on the model created in Backbone.js
         */
        inherits: function (extensions) {
            var me = this;
            var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function () {
                return me.apply(this, arguments);
            };

            var Surrogate = function () {
                this.constructor = ChartElement;
            };

            Surrogate.prototype = me.prototype;
            ChartElement.prototype = new Surrogate();
            ChartElement.extend = helpers.inherits;

            if (extensions) {
                helpers.extend(ChartElement.prototype, extensions);
            }

            ChartElement.__super__ = me.prototype;
            return ChartElement;
        }
    };

    var helpers_core = helpers;

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, use Chart.helpers.callback instead.
     * @function Chart.helpers.callCallback
     * @deprecated since version 2.6.0
     * @todo remove at version 3
     * @private
     */
    helpers.callCallback = helpers.callback;

    /**
     * Provided for backward compatibility, use Array.prototype.indexOf instead.
     * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
     * @function Chart.helpers.indexOf
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers.indexOf = function (array, item, fromIndex) {
        return Array.prototype.indexOf.call(array, item, fromIndex);
    };

    /**
     * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
     * @function Chart.helpers.getValueOrDefault
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers.getValueOrDefault = helpers.valueOrDefault;

    /**
     * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
     * @function Chart.helpers.getValueAtIndexOrDefault
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;

    /**
     * Easing functions adapted from Robert Penner's easing equations.
     * @namespace Chart.helpers.easingEffects
     * @see http://www.robertpenner.com/easing/
     */
    var effects = {
        linear: function (t) {
            return t;
        },

        easeInQuad: function (t) {
            return t * t;
        },

        easeOutQuad: function (t) {
            return -t * (t - 2);
        },

        easeInOutQuad: function (t) {
            if ((t /= 0.5) < 1) {
                return 0.5 * t * t;
            }
            return -0.5 * ((--t) * (t - 2) - 1);
        },

        easeInCubic: function (t) {
            return t * t * t;
        },

        easeOutCubic: function (t) {
            return (t = t - 1) * t * t + 1;
        },

        easeInOutCubic: function (t) {
            if ((t /= 0.5) < 1) {
                return 0.5 * t * t * t;
            }
            return 0.5 * ((t -= 2) * t * t + 2);
        },

        easeInQuart: function (t) {
            return t * t * t * t;
        },

        easeOutQuart: function (t) {
            return -((t = t - 1) * t * t * t - 1);
        },

        easeInOutQuart: function (t) {
            if ((t /= 0.5) < 1) {
                return 0.5 * t * t * t * t;
            }
            return -0.5 * ((t -= 2) * t * t * t - 2);
        },

        easeInQuint: function (t) {
            return t * t * t * t * t;
        },

        easeOutQuint: function (t) {
            return (t = t - 1) * t * t * t * t + 1;
        },

        easeInOutQuint: function (t) {
            if ((t /= 0.5) < 1) {
                return 0.5 * t * t * t * t * t;
            }
            return 0.5 * ((t -= 2) * t * t * t * t + 2);
        },

        easeInSine: function (t) {
            return -Math.cos(t * (Math.PI / 2)) + 1;
        },

        easeOutSine: function (t) {
            return Math.sin(t * (Math.PI / 2));
        },

        easeInOutSine: function (t) {
            return -0.5 * (Math.cos(Math.PI * t) - 1);
        },

        easeInExpo: function (t) {
            return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
        },

        easeOutExpo: function (t) {
            return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
        },

        easeInOutExpo: function (t) {
            if (t === 0) {
                return 0;
            }
            if (t === 1) {
                return 1;
            }
            if ((t /= 0.5) < 1) {
                return 0.5 * Math.pow(2, 10 * (t - 1));
            }
            return 0.5 * (-Math.pow(2, -10 * --t) + 2);
        },

        easeInCirc: function (t) {
            if (t >= 1) {
                return t;
            }
            return -(Math.sqrt(1 - t * t) - 1);
        },

        easeOutCirc: function (t) {
            return Math.sqrt(1 - (t = t - 1) * t);
        },

        easeInOutCirc: function (t) {
            if ((t /= 0.5) < 1) {
                return -0.5 * (Math.sqrt(1 - t * t) - 1);
            }
            return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
        },

        easeInElastic: function (t) {
            var s = 1.70158;
            var p = 0;
            var a = 1;
            if (t === 0) {
                return 0;
            }
            if (t === 1) {
                return 1;
            }
            if (!p) {
                p = 0.3;
            }
            if (a < 1) {
                a = 1;
                s = p / 4;
            } else {
                s = p / (2 * Math.PI) * Math.asin(1 / a);
            }
            return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
        },

        easeOutElastic: function (t) {
            var s = 1.70158;
            var p = 0;
            var a = 1;
            if (t === 0) {
                return 0;
            }
            if (t === 1) {
                return 1;
            }
            if (!p) {
                p = 0.3;
            }
            if (a < 1) {
                a = 1;
                s = p / 4;
            } else {
                s = p / (2 * Math.PI) * Math.asin(1 / a);
            }
            return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
        },

        easeInOutElastic: function (t) {
            var s = 1.70158;
            var p = 0;
            var a = 1;
            if (t === 0) {
                return 0;
            }
            if ((t /= 0.5) === 2) {
                return 1;
            }
            if (!p) {
                p = 0.45;
            }
            if (a < 1) {
                a = 1;
                s = p / 4;
            } else {
                s = p / (2 * Math.PI) * Math.asin(1 / a);
            }
            if (t < 1) {
                return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
            }
            return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
        },
        easeInBack: function (t) {
            var s = 1.70158;
            return t * t * ((s + 1) * t - s);
        },

        easeOutBack: function (t) {
            var s = 1.70158;
            return (t = t - 1) * t * ((s + 1) * t + s) + 1;
        },

        easeInOutBack: function (t) {
            var s = 1.70158;
            if ((t /= 0.5) < 1) {
                return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
            }
            return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
        },

        easeInBounce: function (t) {
            return 1 - effects.easeOutBounce(1 - t);
        },

        easeOutBounce: function (t) {
            if (t < (1 / 2.75)) {
                return 7.5625 * t * t;
            }
            if (t < (2 / 2.75)) {
                return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
            }
            if (t < (2.5 / 2.75)) {
                return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
            }
            return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
        },

        easeInOutBounce: function (t) {
            if (t < 0.5) {
                return effects.easeInBounce(t * 2) * 0.5;
            }
            return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
        }
    };

    var helpers_easing = {
        effects: effects
    };

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, use Chart.helpers.easing.effects instead.
     * @function Chart.helpers.easingEffects
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers_core.easingEffects = effects;

    var PI = Math.PI;
    var RAD_PER_DEG = PI / 180;
    var DOUBLE_PI = PI * 2;
    var HALF_PI = PI / 2;
    var QUARTER_PI = PI / 4;
    var TWO_THIRDS_PI = PI * 2 / 3;

    /**
     * @namespace Chart.helpers.canvas
     */
    var exports$1 = {
        /**
         * Clears the entire canvas associated to the given `chart`.
         * @param {Chart} chart - The chart for which to clear the canvas.
         */
        clear: function (chart) {
            chart.ctx.clearRect(0, 0, chart.width, chart.height);
        },

        /**
         * Creates a "path" for a rectangle with rounded corners at position (x, y) with a
         * given size (width, height) and the same `radius` for all corners.
         * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
         * @param {number} x - The x axis of the coordinate for the rectangle starting point.
         * @param {number} y - The y axis of the coordinate for the rectangle starting point.
         * @param {number} width - The rectangle's width.
         * @param {number} height - The rectangle's height.
         * @param {number} radius - The rounded amount (in pixels) for the four corners.
         * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
         */
        roundedRect: function (ctx, x, y, width, height, radius) {
            if (radius) {
                var r = Math.min(radius, height / 2, width / 2);
                var left = x + r;
                var top = y + r;
                var right = x + width - r;
                var bottom = y + height - r;

                ctx.moveTo(x, top);
                if (left < right && top < bottom) {
                    ctx.arc(left, top, r, -PI, -HALF_PI);
                    ctx.arc(right, top, r, -HALF_PI, 0);
                    ctx.arc(right, bottom, r, 0, HALF_PI);
                    ctx.arc(left, bottom, r, HALF_PI, PI);
                } else if (left < right) {
                    ctx.moveTo(left, y);
                    ctx.arc(right, top, r, -HALF_PI, HALF_PI);
                    ctx.arc(left, top, r, HALF_PI, PI + HALF_PI);
                } else if (top < bottom) {
                    ctx.arc(left, top, r, -PI, 0);
                    ctx.arc(left, bottom, r, 0, PI);
                } else {
                    ctx.arc(left, top, r, -PI, PI);
                }
                ctx.closePath();
                ctx.moveTo(x, y);
            } else {
                ctx.rect(x, y, width, height);
            }
        },

        drawPoint: function (ctx, style, radius, x, y, rotation) {
            var type, xOffset, yOffset, size, cornerRadius;
            var rad = (rotation || 0) * RAD_PER_DEG;

            if (style && typeof style === 'object') {
                type = style.toString();
                if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
                    ctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);
                    return;
                }
            }

            if (isNaN(radius) || radius <= 0) {
                return;
            }

            ctx.beginPath();

            switch (style) {
                // Default includes circle
                default:
                    ctx.arc(x, y, radius, 0, DOUBLE_PI);
                    ctx.closePath();
                    break;
                case 'triangle':
                    ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
                    rad += TWO_THIRDS_PI;
                    ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
                    rad += TWO_THIRDS_PI;
                    ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
                    ctx.closePath();
                    break;
                case 'rectRounded':
                    // NOTE: the rounded rect implementation changed to use `arc` instead of
                    // `quadraticCurveTo` since it generates better results when rect is
                    // almost a circle. 0.516 (instead of 0.5) produces results with visually
                    // closer proportion to the previous impl and it is inscribed in the
                    // circle with `radius`. For more details, see the following PRs:
                    // https://github.com/chartjs/Chart.js/issues/5597
                    // https://github.com/chartjs/Chart.js/issues/5858
                    cornerRadius = radius * 0.516;
                    size = radius - cornerRadius;
                    xOffset = Math.cos(rad + QUARTER_PI) * size;
                    yOffset = Math.sin(rad + QUARTER_PI) * size;
                    ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
                    ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
                    ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
                    ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
                    ctx.closePath();
                    break;
                case 'rect':
                    if (!rotation) {
                        size = Math.SQRT1_2 * radius;
                        ctx.rect(x - size, y - size, 2 * size, 2 * size);
                        break;
                    }
                    rad += QUARTER_PI;
                /* falls through */
                case 'rectRot':
                    xOffset = Math.cos(rad) * radius;
                    yOffset = Math.sin(rad) * radius;
                    ctx.moveTo(x - xOffset, y - yOffset);
                    ctx.lineTo(x + yOffset, y - xOffset);
                    ctx.lineTo(x + xOffset, y + yOffset);
                    ctx.lineTo(x - yOffset, y + xOffset);
                    ctx.closePath();
                    break;
                case 'crossRot':
                    rad += QUARTER_PI;
                /* falls through */
                case 'cross':
                    xOffset = Math.cos(rad) * radius;
                    yOffset = Math.sin(rad) * radius;
                    ctx.moveTo(x - xOffset, y - yOffset);
                    ctx.lineTo(x + xOffset, y + yOffset);
                    ctx.moveTo(x + yOffset, y - xOffset);
                    ctx.lineTo(x - yOffset, y + xOffset);
                    break;
                case 'star':
                    xOffset = Math.cos(rad) * radius;
                    yOffset = Math.sin(rad) * radius;
                    ctx.moveTo(x - xOffset, y - yOffset);
                    ctx.lineTo(x + xOffset, y + yOffset);
                    ctx.moveTo(x + yOffset, y - xOffset);
                    ctx.lineTo(x - yOffset, y + xOffset);
                    rad += QUARTER_PI;
                    xOffset = Math.cos(rad) * radius;
                    yOffset = Math.sin(rad) * radius;
                    ctx.moveTo(x - xOffset, y - yOffset);
                    ctx.lineTo(x + xOffset, y + yOffset);
                    ctx.moveTo(x + yOffset, y - xOffset);
                    ctx.lineTo(x - yOffset, y + xOffset);
                    break;
                case 'line':
                    xOffset = Math.cos(rad) * radius;
                    yOffset = Math.sin(rad) * radius;
                    ctx.moveTo(x - xOffset, y - yOffset);
                    ctx.lineTo(x + xOffset, y + yOffset);
                    break;
                case 'dash':
                    ctx.moveTo(x, y);
                    ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
                    break;
            }

            ctx.fill();
            ctx.stroke();
        },

        /**
         * Returns true if the point is inside the rectangle
         * @param {object} point - The point to test
         * @param {object} area - The rectangle
         * @returns {boolean}
         * @private
         */
        _isPointInArea: function (point, area) {
            var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.

            return point.x > area.left - epsilon && point.x < area.right + epsilon &&
                point.y > area.top - epsilon && point.y < area.bottom + epsilon;
        },

        clipArea: function (ctx, area) {
            ctx.save();
            ctx.beginPath();
            ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
            ctx.clip();
        },

        unclipArea: function (ctx) {
            ctx.restore();
        },

        lineTo: function (ctx, previous, target, flip) {
            var stepped = target.steppedLine;
            if (stepped) {
                if (stepped === 'middle') {
                    var midpoint = (previous.x + target.x) / 2.0;
                    ctx.lineTo(midpoint, flip ? target.y : previous.y);
                    ctx.lineTo(midpoint, flip ? previous.y : target.y);
                } else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {
                    ctx.lineTo(previous.x, target.y);
                } else {
                    ctx.lineTo(target.x, previous.y);
                }
                ctx.lineTo(target.x, target.y);
                return;
            }

            if (!target.tension) {
                ctx.lineTo(target.x, target.y);
                return;
            }

            ctx.bezierCurveTo(
                flip ? previous.controlPointPreviousX : previous.controlPointNextX,
                flip ? previous.controlPointPreviousY : previous.controlPointNextY,
                flip ? target.controlPointNextX : target.controlPointPreviousX,
                flip ? target.controlPointNextY : target.controlPointPreviousY,
                target.x,
                target.y);
        }
    };

    var helpers_canvas = exports$1;

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
     * @namespace Chart.helpers.clear
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers_core.clear = exports$1.clear;

    /**
     * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
     * @namespace Chart.helpers.drawRoundedRectangle
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers_core.drawRoundedRectangle = function (ctx) {
        ctx.beginPath();
        exports$1.roundedRect.apply(exports$1, arguments);
    };

    var defaults = {
        /**
         * @private
         */
        _set: function (scope, values) {
            return helpers_core.merge(this[scope] || (this[scope] = {}), values);
        }
    };

    defaults._set('global', {
        defaultColor: 'rgba(0,0,0,0.1)',
        defaultFontColor: '#666',
        defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
        defaultFontSize: 12,
        defaultFontStyle: 'normal',
        defaultLineHeight: 1.2,
        showLines: true
    });

    var core_defaults = defaults;

    var valueOrDefault = helpers_core.valueOrDefault;

    /**
     * Converts the given font object into a CSS font string.
     * @param {object} font - A font object.
     * @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
     * @private
     */
    function toFontString(font) {
        if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) {
            return null;
        }

        return (font.style ? font.style + ' ' : '')
            + (font.weight ? font.weight + ' ' : '')
            + font.size + 'px '
            + font.family;
    }

    /**
     * @alias Chart.helpers.options
     * @namespace
     */
    var helpers_options = {
        /**
         * Converts the given line height `value` in pixels for a specific font `size`.
         * @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
         * @param {number} size - The font size (in pixels) used to resolve relative `value`.
         * @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).
         * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
         * @since 2.7.0
         */
        toLineHeight: function (value, size) {
            var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
            if (!matches || matches[1] === 'normal') {
                return size * 1.2;
            }

            value = +matches[2];

            switch (matches[3]) {
                case 'px':
                    return value;
                case '%':
                    value /= 100;
                    break;
                default:
                    break;
            }

            return size * value;
        },

        /**
         * Converts the given value into a padding object with pre-computed width/height.
         * @param {number|object} value - If a number, set the value to all TRBL component,
         *  else, if and object, use defined properties and sets undefined ones to 0.
         * @returns {object} The padding values (top, right, bottom, left, width, height)
         * @since 2.7.0
         */
        toPadding: function (value) {
            var t, r, b, l;

            if (helpers_core.isObject(value)) {
                t = +value.top || 0;
                r = +value.right || 0;
                b = +value.bottom || 0;
                l = +value.left || 0;
            } else {
                t = r = b = l = +value || 0;
            }

            return {
                top: t,
                right: r,
                bottom: b,
                left: l,
                height: t + b,
                width: l + r
            };
        },

        /**
         * Parses font options and returns the font object.
         * @param {object} options - A object that contains font options to be parsed.
         * @return {object} The font object.
         * @todo Support font.* options and renamed to toFont().
         * @private
         */
        _parseFont: function (options) {
            var globalDefaults = core_defaults.global;
            var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
            var font = {
                family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),
                lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),
                size: size,
                style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),
                weight: null,
                string: ''
            };

            font.string = toFontString(font);
            return font;
        },

        /**
         * Evaluates the given `inputs` sequentially and returns the first defined value.
         * @param {Array} inputs - An array of values, falling back to the last value.
         * @param {object} [context] - If defined and the current value is a function, the value
         * is called with `context` as first argument and the result becomes the new input.
         * @param {number} [index] - If defined and the current value is an array, the value
         * at `index` become the new input.
         * @since 2.7.0
         */
        resolve: function (inputs, context, index) {
            var i, ilen, value;

            for (i = 0, ilen = inputs.length; i < ilen; ++i) {
                value = inputs[i];
                if (value === undefined) {
                    continue;
                }
                if (context !== undefined && typeof value === 'function') {
                    value = value(context);
                }
                if (index !== undefined && helpers_core.isArray(value)) {
                    value = value[index];
                }
                if (value !== undefined) {
                    return value;
                }
            }
        }
    };

    var helpers$1 = helpers_core;
    var easing = helpers_easing;
    var canvas = helpers_canvas;
    var options = helpers_options;
    helpers$1.easing = easing;
    helpers$1.canvas = canvas;
    helpers$1.options = options;

    function interpolate(start, view, model, ease) {
        var keys = Object.keys(model);
        var i, ilen, key, actual, origin, target, type, c0, c1;

        for (i = 0, ilen = keys.length; i < ilen; ++i) {
            key = keys[i];

            target = model[key];

            // if a value is added to the model after pivot() has been called, the view
            // doesn't contain it, so let's initialize the view to the target value.
            if (!view.hasOwnProperty(key)) {
                view[key] = target;
            }

            actual = view[key];

            if (actual === target || key[0] === '_') {
                continue;
            }

            if (!start.hasOwnProperty(key)) {
                start[key] = actual;
            }

            origin = start[key];

            type = typeof target;

            if (type === typeof origin) {
                if (type === 'string') {
                    c0 = chartjsColor(origin);
                    if (c0.valid) {
                        c1 = chartjsColor(target);
                        if (c1.valid) {
                            view[key] = c1.mix(c0, ease).rgbString();
                            continue;
                        }
                    }
                } else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) {
                    view[key] = origin + (target - origin) * ease;
                    continue;
                }
            }

            view[key] = target;
        }
    }

    var Element = function (configuration) {
        helpers$1.extend(this, configuration);
        this.initialize.apply(this, arguments);
    };

    helpers$1.extend(Element.prototype, {

        initialize: function () {
            this.hidden = false;
        },

        pivot: function () {
            var me = this;
            if (!me._view) {
                me._view = helpers$1.clone(me._model);
            }
            me._start = {};
            return me;
        },

        transition: function (ease) {
            var me = this;
            var model = me._model;
            var start = me._start;
            var view = me._view;

            // No animation -> No Transition
            if (!model || ease === 1) {
                me._view = model;
                me._start = null;
                return me;
            }

            if (!view) {
                view = me._view = {};
            }

            if (!start) {
                start = me._start = {};
            }

            interpolate(start, view, model, ease);

            return me;
        },

        tooltipPosition: function () {
            return {
                x: this._model.x,
                y: this._model.y
            };
        },

        hasValue: function () {
            return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y);
        }
    });

    Element.extend = helpers$1.inherits;

    var core_element = Element;

    var exports$2 = core_element.extend({
        chart: null, // the animation associated chart instance
        currentStep: 0, // the current animation step
        numSteps: 60, // default number of steps
        easing: '', // the easing to use for this animation
        render: null, // render function used by the animation service

        onAnimationProgress: null, // user specified callback to fire on each step of the animation
        onAnimationComplete: null, // user specified callback to fire when the animation finishes
    });

    var core_animation = exports$2;

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, use Chart.Animation instead
     * @prop Chart.Animation#animationObject
     * @deprecated since version 2.6.0
     * @todo remove at version 3
     */
    Object.defineProperty(exports$2.prototype, 'animationObject', {
        get: function () {
            return this;
        }
    });

    /**
     * Provided for backward compatibility, use Chart.Animation#chart instead
     * @prop Chart.Animation#chartInstance
     * @deprecated since version 2.6.0
     * @todo remove at version 3
     */
    Object.defineProperty(exports$2.prototype, 'chartInstance', {
        get: function () {
            return this.chart;
        },
        set: function (value) {
            this.chart = value;
        }
    });

    core_defaults._set('global', {
        animation: {
            duration: 1000,
            easing: 'easeOutQuart',
            onProgress: helpers$1.noop,
            onComplete: helpers$1.noop
        }
    });

    var core_animations = {
        animations: [],
        request: null,

        /**
         * @param {Chart} chart - The chart to animate.
         * @param {Chart.Animation} animation - The animation that we will animate.
         * @param {number} duration - The animation duration in ms.
         * @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
         */
        addAnimation: function (chart, animation, duration, lazy) {
            var animations = this.animations;
            var i, ilen;

            animation.chart = chart;
            animation.startTime = Date.now();
            animation.duration = duration;

            if (!lazy) {
                chart.animating = true;
            }

            for (i = 0, ilen = animations.length; i < ilen; ++i) {
                if (animations[i].chart === chart) {
                    animations[i] = animation;
                    return;
                }
            }

            animations.push(animation);

            // If there are no animations queued, manually kickstart a digest, for lack of a better word
            if (animations.length === 1) {
                this.requestAnimationFrame();
            }
        },

        cancelAnimation: function (chart) {
            var index = helpers$1.findIndex(this.animations, function (animation) {
                return animation.chart === chart;
            });

            if (index !== -1) {
                this.animations.splice(index, 1);
                chart.animating = false;
            }
        },

        requestAnimationFrame: function () {
            var me = this;
            if (me.request === null) {
                // Skip animation frame requests until the active one is executed.
                // This can happen when processing mouse events, e.g. 'mousemove'
                // and 'mouseout' events will trigger multiple renders.
                me.request = helpers$1.requestAnimFrame.call(window, function () {
                    me.request = null;
                    me.startDigest();
                });
            }
        },

        /**
         * @private
         */
        startDigest: function () {
            var me = this;

            me.advance();

            // Do we have more stuff to animate?
            if (me.animations.length > 0) {
                me.requestAnimationFrame();
            }
        },

        /**
         * @private
         */
        advance: function () {
            var animations = this.animations;
            var animation, chart, numSteps, nextStep;
            var i = 0;

            // 1 animation per chart, so we are looping charts here
            while (i < animations.length) {
                animation = animations[i];
                chart = animation.chart;
                numSteps = animation.numSteps;

                // Make sure that currentStep starts at 1
                // https://github.com/chartjs/Chart.js/issues/6104
                nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1;
                animation.currentStep = Math.min(nextStep, numSteps);

                helpers$1.callback(animation.render, [chart, animation], chart);
                helpers$1.callback(animation.onAnimationProgress, [animation], chart);

                if (animation.currentStep >= numSteps) {
                    helpers$1.callback(animation.onAnimationComplete, [animation], chart);
                    chart.animating = false;
                    animations.splice(i, 1);
                } else {
                    ++i;
                }
            }
        }
    };

    var resolve = helpers$1.options.resolve;

    var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];

    /**
     * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
     * 'unshift') and notify the listener AFTER the array has been altered. Listeners are
     * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
     */
    function listenArrayEvents(array, listener) {
        if (array._chartjs) {
            array._chartjs.listeners.push(listener);
            return;
        }

        Object.defineProperty(array, '_chartjs', {
            configurable: true,
            enumerable: false,
            value: {
                listeners: [listener]
            }
        });

        arrayEvents.forEach(function (key) {
            var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
            var base = array[key];

            Object.defineProperty(array, key, {
                configurable: true,
                enumerable: false,
                value: function () {
                    var args = Array.prototype.slice.call(arguments);
                    var res = base.apply(this, args);

                    helpers$1.each(array._chartjs.listeners, function (object) {
                        if (typeof object[method] === 'function') {
                            object[method].apply(object, args);
                        }
                    });

                    return res;
                }
            });
        });
    }

    /**
     * Removes the given array event listener and cleanup extra attached properties (such as
     * the _chartjs stub and overridden methods) if array doesn't have any more listeners.
     */
    function unlistenArrayEvents(array, listener) {
        var stub = array._chartjs;
        if (!stub) {
            return;
        }

        var listeners = stub.listeners;
        var index = listeners.indexOf(listener);
        if (index !== -1) {
            listeners.splice(index, 1);
        }

        if (listeners.length > 0) {
            return;
        }

        arrayEvents.forEach(function (key) {
            delete array[key];
        });

        delete array._chartjs;
    }

    // Base class for all dataset controllers (line, bar, etc)
    var DatasetController = function (chart, datasetIndex) {
        this.initialize(chart, datasetIndex);
    };

    helpers$1.extend(DatasetController.prototype, {

        /**
         * Element type used to generate a meta dataset (e.g. Chart.element.Line).
         * @type {Chart.core.element}
         */
        datasetElementType: null,

        /**
         * Element type used to generate a meta data (e.g. Chart.element.Point).
         * @type {Chart.core.element}
         */
        dataElementType: null,

        initialize: function (chart, datasetIndex) {
            var me = this;
            me.chart = chart;
            me.index = datasetIndex;
            me.linkScales();
            me.addElements();
        },

        updateIndex: function (datasetIndex) {
            this.index = datasetIndex;
        },

        linkScales: function () {
            var me = this;
            var meta = me.getMeta();
            var dataset = me.getDataset();

            if (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {
                meta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;
            }
            if (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {
                meta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;
            }
        },

        getDataset: function () {
            return this.chart.data.datasets[this.index];
        },

        getMeta: function () {
            return this.chart.getDatasetMeta(this.index);
        },

        getScaleForId: function (scaleID) {
            return this.chart.scales[scaleID];
        },

        /**
         * @private
         */
        _getValueScaleId: function () {
            return this.getMeta().yAxisID;
        },

        /**
         * @private
         */
        _getIndexScaleId: function () {
            return this.getMeta().xAxisID;
        },

        /**
         * @private
         */
        _getValueScale: function () {
            return this.getScaleForId(this._getValueScaleId());
        },

        /**
         * @private
         */
        _getIndexScale: function () {
            return this.getScaleForId(this._getIndexScaleId());
        },

        reset: function () {
            this.update(true);
        },

        /**
         * @private
         */
        destroy: function () {
            if (this._data) {
                unlistenArrayEvents(this._data, this);
            }
        },

        createMetaDataset: function () {
            var me = this;
            var type = me.datasetElementType;
            return type && new type({
                _chart: me.chart,
                _datasetIndex: me.index
            });
        },

        createMetaData: function (index) {
            var me = this;
            var type = me.dataElementType;
            return type && new type({
                _chart: me.chart,
                _datasetIndex: me.index,
                _index: index
            });
        },

        addElements: function () {
            var me = this;
            var meta = me.getMeta();
            var data = me.getDataset().data || [];
            var metaData = meta.data;
            var i, ilen;

            for (i = 0, ilen = data.length; i < ilen; ++i) {
                metaData[i] = metaData[i] || me.createMetaData(i);
            }

            meta.dataset = meta.dataset || me.createMetaDataset();
        },

        addElementAndReset: function (index) {
            var element = this.createMetaData(index);
            this.getMeta().data.splice(index, 0, element);
            this.updateElement(element, index, true);
        },

        buildOrUpdateElements: function () {
            var me = this;
            var dataset = me.getDataset();
            var data = dataset.data || (dataset.data = []);

            // In order to correctly handle data addition/deletion animation (an thus simulate
            // real-time charts), we need to monitor these data modifications and synchronize
            // the internal meta data accordingly.
            if (me._data !== data) {
                if (me._data) {
                    // This case happens when the user replaced the data array instance.
                    unlistenArrayEvents(me._data, me);
                }

                if (data && Object.isExtensible(data)) {
                    listenArrayEvents(data, me);
                }
                me._data = data;
            }

            // Re-sync meta data in case the user replaced the data array or if we missed
            // any updates and so make sure that we handle number of datapoints changing.
            me.resyncElements();
        },

        update: helpers$1.noop,

        transition: function (easingValue) {
            var meta = this.getMeta();
            var elements = meta.data || [];
            var ilen = elements.length;
            var i = 0;

            for (; i < ilen; ++i) {
                elements[i].transition(easingValue);
            }

            if (meta.dataset) {
                meta.dataset.transition(easingValue);
            }
        },

        draw: function () {
            var meta = this.getMeta();
            var elements = meta.data || [];
            var ilen = elements.length;
            var i = 0;

            if (meta.dataset) {
                meta.dataset.draw();
            }

            for (; i < ilen; ++i) {
                elements[i].draw();
            }
        },

        removeHoverStyle: function (element) {
            helpers$1.merge(element._model, element.$previousStyle || {});
            delete element.$previousStyle;
        },

        setHoverStyle: function (element) {
            var dataset = this.chart.data.datasets[element._datasetIndex];
            var index = element._index;
            var custom = element.custom || {};
            var model = element._model;
            var getHoverColor = helpers$1.getHoverColor;

            element.$previousStyle = {
                backgroundColor: model.backgroundColor,
                borderColor: model.borderColor,
                borderWidth: model.borderWidth
            };

            model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index);
            model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index);
            model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index);
        },

        /**
         * @private
         */
        resyncElements: function () {
            var me = this;
            var meta = me.getMeta();
            var data = me.getDataset().data;
            var numMeta = meta.data.length;
            var numData = data.length;

            if (numData < numMeta) {
                meta.data.splice(numData, numMeta - numData);
            } else if (numData > numMeta) {
                me.insertElements(numMeta, numData - numMeta);
            }
        },

        /**
         * @private
         */
        insertElements: function (start, count) {
            for (var i = 0; i < count; ++i) {
                this.addElementAndReset(start + i);
            }
        },

        /**
         * @private
         */
        onDataPush: function () {
            var count = arguments.length;
            this.insertElements(this.getDataset().data.length - count, count);
        },

        /**
         * @private
         */
        onDataPop: function () {
            this.getMeta().data.pop();
        },

        /**
         * @private
         */
        onDataShift: function () {
            this.getMeta().data.shift();
        },

        /**
         * @private
         */
        onDataSplice: function (start, count) {
            this.getMeta().data.splice(start, count);
            this.insertElements(start, arguments.length - 2);
        },

        /**
         * @private
         */
        onDataUnshift: function () {
            this.insertElements(0, arguments.length);
        }
    });

    DatasetController.extend = helpers$1.inherits;

    var core_datasetController = DatasetController;

    core_defaults._set('global', {
        elements: {
            arc: {
                backgroundColor: core_defaults.global.defaultColor,
                borderColor: '#fff',
                borderWidth: 2,
                borderAlign: 'center'
            }
        }
    });

    var element_arc = core_element.extend({
        inLabelRange: function (mouseX) {
            var vm = this._view;

            if (vm) {
                return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
            }
            return false;
        },

        inRange: function (chartX, chartY) {
            var vm = this._view;

            if (vm) {
                var pointRelativePosition = helpers$1.getAngleFromPoint(vm, { x: chartX, y: chartY });
                var angle = pointRelativePosition.angle;
                var distance = pointRelativePosition.distance;

                // Sanitise angle range
                var startAngle = vm.startAngle;
                var endAngle = vm.endAngle;
                while (endAngle < startAngle) {
                    endAngle += 2.0 * Math.PI;
                }
                while (angle > endAngle) {
                    angle -= 2.0 * Math.PI;
                }
                while (angle < startAngle) {
                    angle += 2.0 * Math.PI;
                }

                // Check if within the range of the open/close angle
                var betweenAngles = (angle >= startAngle && angle <= endAngle);
                var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);

                return (betweenAngles && withinRadius);
            }
            return false;
        },

        getCenterPoint: function () {
            var vm = this._view;
            var halfAngle = (vm.startAngle + vm.endAngle) / 2;
            var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
            return {
                x: vm.x + Math.cos(halfAngle) * halfRadius,
                y: vm.y + Math.sin(halfAngle) * halfRadius
            };
        },

        getArea: function () {
            var vm = this._view;
            return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
        },

        tooltipPosition: function () {
            var vm = this._view;
            var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
            var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;

            return {
                x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
                y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
            };
        },

        draw: function () {
            var ctx = this._chart.ctx;
            var vm = this._view;
            var sA = vm.startAngle;
            var eA = vm.endAngle;
            var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
            var angleMargin;

            ctx.save();

            ctx.beginPath();
            ctx.arc(vm.x, vm.y, Math.max(vm.outerRadius - pixelMargin, 0), sA, eA);
            ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
            ctx.closePath();

            ctx.fillStyle = vm.backgroundColor;
            ctx.fill();

            if (vm.borderWidth) {
                if (vm.borderAlign === 'inner') {
                    // Draw an inner border by cliping the arc and drawing a double-width border
                    // Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
                    ctx.beginPath();
                    angleMargin = pixelMargin / vm.outerRadius;
                    ctx.arc(vm.x, vm.y, vm.outerRadius, sA - angleMargin, eA + angleMargin);
                    if (vm.innerRadius > pixelMargin) {
                        angleMargin = pixelMargin / vm.innerRadius;
                        ctx.arc(vm.x, vm.y, vm.innerRadius - pixelMargin, eA + angleMargin, sA - angleMargin, true);
                    } else {
                        ctx.arc(vm.x, vm.y, pixelMargin, eA + Math.PI / 2, sA - Math.PI / 2);
                    }
                    ctx.closePath();
                    ctx.clip();

                    ctx.beginPath();
                    ctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);
                    ctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);
                    ctx.closePath();

                    ctx.lineWidth = vm.borderWidth * 2;
                    ctx.lineJoin = 'round';
                } else {
                    ctx.lineWidth = vm.borderWidth;
                    ctx.lineJoin = 'bevel';
                }

                ctx.strokeStyle = vm.borderColor;
                ctx.stroke();
            }

            ctx.restore();
        }
    });

    var valueOrDefault$1 = helpers$1.valueOrDefault;

    var defaultColor = core_defaults.global.defaultColor;

    core_defaults._set('global', {
        elements: {
            line: {
                tension: 0.4,
                backgroundColor: defaultColor,
                borderWidth: 3,
                borderColor: defaultColor,
                borderCapStyle: 'butt',
                borderDash: [],
                borderDashOffset: 0.0,
                borderJoinStyle: 'miter',
                capBezierPoints: true,
                fill: true, // do we fill in the area between the line and its base axis
            }
        }
    });

    var element_line = core_element.extend({
        draw: function () {
            var me = this;
            var vm = me._view;
            var ctx = me._chart.ctx;
            var spanGaps = vm.spanGaps;
            var points = me._children.slice(); // clone array
            var globalDefaults = core_defaults.global;
            var globalOptionLineElements = globalDefaults.elements.line;
            var lastDrawnIndex = -1;
            var index, current, previous, currentVM;

            // If we are looping, adding the first point again
            if (me._loop && points.length) {
                points.push(points[0]);
            }

            ctx.save();

            // Stroke Line Options
            ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;

            // IE 9 and 10 do not support line dash
            if (ctx.setLineDash) {
                ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
            }

            ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset);
            ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
            ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth);
            ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;

            // Stroke Line
            ctx.beginPath();
            lastDrawnIndex = -1;

            for (index = 0; index < points.length; ++index) {
                current = points[index];
                previous = helpers$1.previousItem(points, index);
                currentVM = current._view;

                // First point moves to it's starting position no matter what
                if (index === 0) {
                    if (!currentVM.skip) {
                        ctx.moveTo(currentVM.x, currentVM.y);
                        lastDrawnIndex = index;
                    }
                } else {
                    previous = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];

                    if (!currentVM.skip) {
                        if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
                            // There was a gap and this is the first point after the gap
                            ctx.moveTo(currentVM.x, currentVM.y);
                        } else {
                            // Line to next point
                            helpers$1.canvas.lineTo(ctx, previous._view, current._view);
                        }
                        lastDrawnIndex = index;
                    }
                }
            }

            ctx.stroke();
            ctx.restore();
        }
    });

    var valueOrDefault$2 = helpers$1.valueOrDefault;

    var defaultColor$1 = core_defaults.global.defaultColor;

    core_defaults._set('global', {
        elements: {
            point: {
                radius: 3,
                pointStyle: 'circle',
                backgroundColor: defaultColor$1,
                borderColor: defaultColor$1,
                borderWidth: 1,
                // Hover
                hitRadius: 1,
                hoverRadius: 4,
                hoverBorderWidth: 1
            }
        }
    });

    function xRange(mouseX) {
        var vm = this._view;
        return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
    }

    function yRange(mouseY) {
        var vm = this._view;
        return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
    }

    var element_point = core_element.extend({
        inRange: function (mouseX, mouseY) {
            var vm = this._view;
            return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
        },

        inLabelRange: xRange,
        inXRange: xRange,
        inYRange: yRange,

        getCenterPoint: function () {
            var vm = this._view;
            return {
                x: vm.x,
                y: vm.y
            };
        },

        getArea: function () {
            return Math.PI * Math.pow(this._view.radius, 2);
        },

        tooltipPosition: function () {
            var vm = this._view;
            return {
                x: vm.x,
                y: vm.y,
                padding: vm.radius + vm.borderWidth
            };
        },

        draw: function (chartArea) {
            var vm = this._view;
            var ctx = this._chart.ctx;
            var pointStyle = vm.pointStyle;
            var rotation = vm.rotation;
            var radius = vm.radius;
            var x = vm.x;
            var y = vm.y;
            var globalDefaults = core_defaults.global;
            var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow

            if (vm.skip) {
                return;
            }

            // Clipping for Points.
            if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) {
                ctx.strokeStyle = vm.borderColor || defaultColor;
                ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth);
                ctx.fillStyle = vm.backgroundColor || defaultColor;
                helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
            }
        }
    });

    var defaultColor$2 = core_defaults.global.defaultColor;

    core_defaults._set('global', {
        elements: {
            rectangle: {
                backgroundColor: defaultColor$2,
                borderColor: defaultColor$2,
                borderSkipped: 'bottom',
                borderWidth: 0
            }
        }
    });

    function isVertical(vm) {
        return vm && vm.width !== undefined;
    }

    /**
     * Helper function to get the bounds of the bar regardless of the orientation
     * @param bar {Chart.Element.Rectangle} the bar
     * @return {Bounds} bounds of the bar
     * @private
     */
    function getBarBounds(vm) {
        var x1, x2, y1, y2, half;

        if (isVertical(vm)) {
            half = vm.width / 2;
            x1 = vm.x - half;
            x2 = vm.x + half;
            y1 = Math.min(vm.y, vm.base);
            y2 = Math.max(vm.y, vm.base);
        } else {
            half = vm.height / 2;
            x1 = Math.min(vm.x, vm.base);
            x2 = Math.max(vm.x, vm.base);
            y1 = vm.y - half;
            y2 = vm.y + half;
        }

        return {
            left: x1,
            top: y1,
            right: x2,
            bottom: y2
        };
    }

    function swap(orig, v1, v2) {
        return orig === v1 ? v2 : orig === v2 ? v1 : orig;
    }

    function parseBorderSkipped(vm) {
        var edge = vm.borderSkipped;
        var res = {};

        if (!edge) {
            return res;
        }

        if (vm.horizontal) {
            if (vm.base > vm.x) {
                edge = swap(edge, 'left', 'right');
            }
        } else if (vm.base < vm.y) {
            edge = swap(edge, 'bottom', 'top');
        }

        res[edge] = true;
        return res;
    }

    function parseBorderWidth(vm, maxW, maxH) {
        var value = vm.borderWidth;
        var skip = parseBorderSkipped(vm);
        var t, r, b, l;

        if (helpers$1.isObject(value)) {
            t = +value.top || 0;
            r = +value.right || 0;
            b = +value.bottom || 0;
            l = +value.left || 0;
        } else {
            t = r = b = l = +value || 0;
        }

        return {
            t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t,
            r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r,
            b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b,
            l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l
        };
    }

    function boundingRects(vm) {
        var bounds = getBarBounds(vm);
        var width = bounds.right - bounds.left;
        var height = bounds.bottom - bounds.top;
        var border = parseBorderWidth(vm, width / 2, height / 2);

        return {
            outer: {
                x: bounds.left,
                y: bounds.top,
                w: width,
                h: height
            },
            inner: {
                x: bounds.left + border.l,
                y: bounds.top + border.t,
                w: width - border.l - border.r,
                h: height - border.t - border.b
            }
        };
    }

    function inRange(vm, x, y) {
        var skipX = x === null;
        var skipY = y === null;
        var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm);

        return bounds
            && (skipX || x >= bounds.left && x <= bounds.right)
            && (skipY || y >= bounds.top && y <= bounds.bottom);
    }

    var element_rectangle = core_element.extend({
        draw: function () {
            var ctx = this._chart.ctx;
            var vm = this._view;
            var rects = boundingRects(vm);
            var outer = rects.outer;
            var inner = rects.inner;

            ctx.fillStyle = vm.backgroundColor;
            ctx.fillRect(outer.x, outer.y, outer.w, outer.h);

            if (outer.w === inner.w && outer.h === inner.h) {
                return;
            }

            ctx.save();
            ctx.beginPath();
            ctx.rect(outer.x, outer.y, outer.w, outer.h);
            ctx.clip();
            ctx.fillStyle = vm.borderColor;
            ctx.rect(inner.x, inner.y, inner.w, inner.h);
            ctx.fill('evenodd');
            ctx.restore();
        },

        height: function () {
            var vm = this._view;
            return vm.base - vm.y;
        },

        inRange: function (mouseX, mouseY) {
            return inRange(this._view, mouseX, mouseY);
        },

        inLabelRange: function (mouseX, mouseY) {
            var vm = this._view;
            return isVertical(vm)
                ? inRange(vm, mouseX, null)
                : inRange(vm, null, mouseY);
        },

        inXRange: function (mouseX) {
            return inRange(this._view, mouseX, null);
        },

        inYRange: function (mouseY) {
            return inRange(this._view, null, mouseY);
        },

        getCenterPoint: function () {
            var vm = this._view;
            var x, y;
            if (isVertical(vm)) {
                x = vm.x;
                y = (vm.y + vm.base) / 2;
            } else {
                x = (vm.x + vm.base) / 2;
                y = vm.y;
            }

            return { x: x, y: y };
        },

        getArea: function () {
            var vm = this._view;

            return isVertical(vm)
                ? vm.width * Math.abs(vm.y - vm.base)
                : vm.height * Math.abs(vm.x - vm.base);
        },

        tooltipPosition: function () {
            var vm = this._view;
            return {
                x: vm.x,
                y: vm.y
            };
        }
    });

    var elements = {};
    var Arc = element_arc;
    var Line = element_line;
    var Point = element_point;
    var Rectangle = element_rectangle;
    elements.Arc = Arc;
    elements.Line = Line;
    elements.Point = Point;
    elements.Rectangle = Rectangle;

    var resolve$1 = helpers$1.options.resolve;

    core_defaults._set('bar', {
        hover: {
            mode: 'label'
        },

        scales: {
            xAxes: [{
                type: 'category',
                categoryPercentage: 0.8,
                barPercentage: 0.9,
                offset: true,
                gridLines: {
                    offsetGridLines: true
                }
            }],

            yAxes: [{
                type: 'linear'
            }]
        }
    });

    /**
     * Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
     * @private
     */
    function computeMinSampleSize(scale, pixels) {
        var min = scale.isHorizontal() ? scale.width : scale.height;
        var ticks = scale.getTicks();
        var prev, curr, i, ilen;

        for (i = 1, ilen = pixels.length; i < ilen; ++i) {
            min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
        }

        for (i = 0, ilen = ticks.length; i < ilen; ++i) {
            curr = scale.getPixelForTick(i);
            min = i > 0 ? Math.min(min, curr - prev) : min;
            prev = curr;
        }

        return min;
    }

    /**
     * Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
     * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
     * mode currently always generates bars equally sized (until we introduce scriptable options?).
     * @private
     */
    function computeFitCategoryTraits(index, ruler, options) {
        var thickness = options.barThickness;
        var count = ruler.stackCount;
        var curr = ruler.pixels[index];
        var size, ratio;

        if (helpers$1.isNullOrUndef(thickness)) {
            size = ruler.min * options.categoryPercentage;
            ratio = options.barPercentage;
        } else {
            // When bar thickness is enforced, category and bar percentages are ignored.
            // Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
            // and deprecate barPercentage since this value is ignored when thickness is absolute.
            size = thickness * count;
            ratio = 1;
        }

        return {
            chunk: size / count,
            ratio: ratio,
            start: curr - (size / 2)
        };
    }

    /**
     * Computes an "optimal" category that globally arranges bars side by side (no gap when
     * percentage options are 1), based on the previous and following categories. This mode
     * generates bars with different widths when data are not evenly spaced.
     * @private
     */
    function computeFlexCategoryTraits(index, ruler, options) {
        var pixels = ruler.pixels;
        var curr = pixels[index];
        var prev = index > 0 ? pixels[index - 1] : null;
        var next = index < pixels.length - 1 ? pixels[index + 1] : null;
        var percent = options.categoryPercentage;
        var start, size;

        if (prev === null) {
            // first data: its size is double based on the next point or,
            // if it's also the last data, we use the scale size.
            prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
        }

        if (next === null) {
            // last data: its size is also double based on the previous point.
            next = curr + curr - prev;
        }

        start = curr - (curr - Math.min(prev, next)) / 2 * percent;
        size = Math.abs(next - prev) / 2 * percent;

        return {
            chunk: size / ruler.stackCount,
            ratio: options.barPercentage,
            start: start
        };
    }

    var controller_bar = core_datasetController.extend({

        dataElementType: elements.Rectangle,

        initialize: function () {
            var me = this;
            var meta;

            core_datasetController.prototype.initialize.apply(me, arguments);

            meta = me.getMeta();
            meta.stack = me.getDataset().stack;
            meta.bar = true;
        },

        update: function (reset) {
            var me = this;
            var rects = me.getMeta().data;
            var i, ilen;

            me._ruler = me.getRuler();

            for (i = 0, ilen = rects.length; i < ilen; ++i) {
                me.updateElement(rects[i], i, reset);
            }
        },

        updateElement: function (rectangle, index, reset) {
            var me = this;
            var meta = me.getMeta();
            var dataset = me.getDataset();
            var options = me._resolveElementOptions(rectangle, index);

            rectangle._xScale = me.getScaleForId(meta.xAxisID);
            rectangle._yScale = me.getScaleForId(meta.yAxisID);
            rectangle._datasetIndex = me.index;
            rectangle._index = index;
            rectangle._model = {
                backgroundColor: options.backgroundColor,
                borderColor: options.borderColor,
                borderSkipped: options.borderSkipped,
                borderWidth: options.borderWidth,
                datasetLabel: dataset.label,
                label: me.chart.data.labels[index]
            };

            me._updateElementGeometry(rectangle, index, reset);

            rectangle.pivot();
        },

        /**
         * @private
         */
        _updateElementGeometry: function (rectangle, index, reset) {
            var me = this;
            var model = rectangle._model;
            var vscale = me._getValueScale();
            var base = vscale.getBasePixel();
            var horizontal = vscale.isHorizontal();
            var ruler = me._ruler || me.getRuler();
            var vpixels = me.calculateBarValuePixels(me.index, index);
            var ipixels = me.calculateBarIndexPixels(me.index, index, ruler);

            model.horizontal = horizontal;
            model.base = reset ? base : vpixels.base;
            model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
            model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
            model.height = horizontal ? ipixels.size : undefined;
            model.width = horizontal ? undefined : ipixels.size;
        },

        /**
         * Returns the stacks based on groups and bar visibility.
         * @param {number} [last] - The dataset index
         * @returns {string[]} The list of stack IDs
         * @private
         */
        _getStacks: function (last) {
            var me = this;
            var chart = me.chart;
            var scale = me._getIndexScale();
            var stacked = scale.options.stacked;
            var ilen = last === undefined ? chart.data.datasets.length : last + 1;
            var stacks = [];
            var i, meta;

            for (i = 0; i < ilen; ++i) {
                meta = chart.getDatasetMeta(i);
                if (meta.bar && chart.isDatasetVisible(i) &&
                    (stacked === false ||
                        (stacked === true && stacks.indexOf(meta.stack) === -1) ||
                        (stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {
                    stacks.push(meta.stack);
                }
            }

            return stacks;
        },

        /**
         * Returns the effective number of stacks based on groups and bar visibility.
         * @private
         */
        getStackCount: function () {
            return this._getStacks().length;
        },

        /**
         * Returns the stack index for the given dataset based on groups and bar visibility.
         * @param {number} [datasetIndex] - The dataset index
         * @param {string} [name] - The stack name to find
         * @returns {number} The stack index
         * @private
         */
        getStackIndex: function (datasetIndex, name) {
            var stacks = this._getStacks(datasetIndex);
            var index = (name !== undefined)
                ? stacks.indexOf(name)
                : -1; // indexOf returns -1 if element is not present

            return (index === -1)
                ? stacks.length - 1
                : index;
        },

        /**
         * @private
         */
        getRuler: function () {
            var me = this;
            var scale = me._getIndexScale();
            var stackCount = me.getStackCount();
            var datasetIndex = me.index;
            var isHorizontal = scale.isHorizontal();
            var start = isHorizontal ? scale.left : scale.top;
            var end = start + (isHorizontal ? scale.width : scale.height);
            var pixels = [];
            var i, ilen, min;

            for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
                pixels.push(scale.getPixelForValue(null, i, datasetIndex));
            }

            min = helpers$1.isNullOrUndef(scale.options.barThickness)
                ? computeMinSampleSize(scale, pixels)
                : -1;

            return {
                min: min,
                pixels: pixels,
                start: start,
                end: end,
                stackCount: stackCount,
                scale: scale
            };
        },

        /**
         * Note: pixel values are not clamped to the scale area.
         * @private
         */
        calculateBarValuePixels: function (datasetIndex, index) {
            var me = this;
            var chart = me.chart;
            var meta = me.getMeta();
            var scale = me._getValueScale();
            var isHorizontal = scale.isHorizontal();
            var datasets = chart.data.datasets;
            var value = +scale.getRightValue(datasets[datasetIndex].data[index]);
            var minBarLength = scale.options.minBarLength;
            var stacked = scale.options.stacked;
            var stack = meta.stack;
            var start = 0;
            var i, imeta, ivalue, base, head, size;

            if (stacked || (stacked === undefined && stack !== undefined)) {
                for (i = 0; i < datasetIndex; ++i) {
                    imeta = chart.getDatasetMeta(i);

                    if (imeta.bar &&
                        imeta.stack === stack &&
                        imeta.controller._getValueScaleId() === scale.id &&
                        chart.isDatasetVisible(i)) {

                        ivalue = +scale.getRightValue(datasets[i].data[index]);
                        if ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {
                            start += ivalue;
                        }
                    }
                }
            }

            base = scale.getPixelForValue(start);
            head = scale.getPixelForValue(start + value);
            size = head - base;

            if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
                size = minBarLength;
                if (value >= 0 && !isHorizontal || value < 0 && isHorizontal) {
                    head = base - minBarLength;
                } else {
                    head = base + minBarLength;
                }
            }

            return {
                size: size,
                base: base,
                head: head,
                center: head + size / 2
            };
        },

        /**
         * @private
         */
        calculateBarIndexPixels: function (datasetIndex, index, ruler) {
            var me = this;
            var options = ruler.scale.options;
            var range = options.barThickness === 'flex'
                ? computeFlexCategoryTraits(index, ruler, options)
                : computeFitCategoryTraits(index, ruler, options);

            var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
            var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
            var size = Math.min(
                helpers$1.valueOrDefault(options.maxBarThickness, Infinity),
                range.chunk * range.ratio);

            return {
                base: center - size / 2,
                head: center + size / 2,
                center: center,
                size: size
            };
        },

        draw: function () {
            var me = this;
            var chart = me.chart;
            var scale = me._getValueScale();
            var rects = me.getMeta().data;
            var dataset = me.getDataset();
            var ilen = rects.length;
            var i = 0;

            helpers$1.canvas.clipArea(chart.ctx, chart.chartArea);

            for (; i < ilen; ++i) {
                if (!isNaN(scale.getRightValue(dataset.data[i]))) {
                    rects[i].draw();
                }
            }

            helpers$1.canvas.unclipArea(chart.ctx);
        },

        /**
         * @private
         */
        _resolveElementOptions: function (rectangle, index) {
            var me = this;
            var chart = me.chart;
            var datasets = chart.data.datasets;
            var dataset = datasets[me.index];
            var custom = rectangle.custom || {};
            var options = chart.options.elements.rectangle;
            var values = {};
            var i, ilen, key;

            // Scriptable options
            var context = {
                chart: chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            var keys = [
                'backgroundColor',
                'borderColor',
                'borderSkipped',
                'borderWidth'
            ];

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$1([
                    custom[key],
                    dataset[key],
                    options[key]
                ], context, index);
            }

            return values;
        }
    });

    var valueOrDefault$3 = helpers$1.valueOrDefault;
    var resolve$2 = helpers$1.options.resolve;

    core_defaults._set('bubble', {
        hover: {
            mode: 'single'
        },

        scales: {
            xAxes: [{
                type: 'linear', // bubble should probably use a linear scale by default
                position: 'bottom',
                id: 'x-axis-0' // need an ID so datasets can reference the scale
            }],
            yAxes: [{
                type: 'linear',
                position: 'left',
                id: 'y-axis-0'
            }]
        },

        tooltips: {
            callbacks: {
                title: function () {
                    // Title doesn't make sense for scatter since we format the data as a point
                    return '';
                },
                label: function (item, data) {
                    var datasetLabel = data.datasets[item.datasetIndex].label || '';
                    var dataPoint = data.datasets[item.datasetIndex].data[item.index];
                    return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
                }
            }
        }
    });

    var controller_bubble = core_datasetController.extend({
        /**
         * @protected
         */
        dataElementType: elements.Point,

        /**
         * @protected
         */
        update: function (reset) {
            var me = this;
            var meta = me.getMeta();
            var points = meta.data;

            // Update Points
            helpers$1.each(points, function (point, index) {
                me.updateElement(point, index, reset);
            });
        },

        /**
         * @protected
         */
        updateElement: function (point, index, reset) {
            var me = this;
            var meta = me.getMeta();
            var custom = point.custom || {};
            var xScale = me.getScaleForId(meta.xAxisID);
            var yScale = me.getScaleForId(meta.yAxisID);
            var options = me._resolveElementOptions(point, index);
            var data = me.getDataset().data[index];
            var dsIndex = me.index;

            var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
            var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);

            point._xScale = xScale;
            point._yScale = yScale;
            point._options = options;
            point._datasetIndex = dsIndex;
            point._index = index;
            point._model = {
                backgroundColor: options.backgroundColor,
                borderColor: options.borderColor,
                borderWidth: options.borderWidth,
                hitRadius: options.hitRadius,
                pointStyle: options.pointStyle,
                rotation: options.rotation,
                radius: reset ? 0 : options.radius,
                skip: custom.skip || isNaN(x) || isNaN(y),
                x: x,
                y: y,
            };

            point.pivot();
        },

        /**
         * @protected
         */
        setHoverStyle: function (point) {
            var model = point._model;
            var options = point._options;
            var getHoverColor = helpers$1.getHoverColor;

            point.$previousStyle = {
                backgroundColor: model.backgroundColor,
                borderColor: model.borderColor,
                borderWidth: model.borderWidth,
                radius: model.radius
            };

            model.backgroundColor = valueOrDefault$3(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
            model.borderColor = valueOrDefault$3(options.hoverBorderColor, getHoverColor(options.borderColor));
            model.borderWidth = valueOrDefault$3(options.hoverBorderWidth, options.borderWidth);
            model.radius = options.radius + options.hoverRadius;
        },

        /**
         * @private
         */
        _resolveElementOptions: function (point, index) {
            var me = this;
            var chart = me.chart;
            var datasets = chart.data.datasets;
            var dataset = datasets[me.index];
            var custom = point.custom || {};
            var options = chart.options.elements.point;
            var data = dataset.data[index];
            var values = {};
            var i, ilen, key;

            // Scriptable options
            var context = {
                chart: chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            var keys = [
                'backgroundColor',
                'borderColor',
                'borderWidth',
                'hoverBackgroundColor',
                'hoverBorderColor',
                'hoverBorderWidth',
                'hoverRadius',
                'hitRadius',
                'pointStyle',
                'rotation'
            ];

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$2([
                    custom[key],
                    dataset[key],
                    options[key]
                ], context, index);
            }

            // Custom radius resolution
            values.radius = resolve$2([
                custom.radius,
                data ? data.r : undefined,
                dataset.radius,
                options.radius
            ], context, index);

            return values;
        }
    });

    var resolve$3 = helpers$1.options.resolve;
    var valueOrDefault$4 = helpers$1.valueOrDefault;

    core_defaults._set('doughnut', {
        animation: {
            // Boolean - Whether we animate the rotation of the Doughnut
            animateRotate: true,
            // Boolean - Whether we animate scaling the Doughnut from the centre
            animateScale: false
        },
        hover: {
            mode: 'single'
        },
        legendCallback: function (chart) {
            var text = [];
            text.push('<ul class="' + chart.id + '-legend">');

            var data = chart.data;
            var datasets = data.datasets;
            var labels = data.labels;

            if (datasets.length) {
                for (var i = 0; i < datasets[0].data.length; ++i) {
                    text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
                    if (labels[i]) {
                        text.push(labels[i]);
                    }
                    text.push('</li>');
                }
            }

            text.push('</ul>');
            return text.join('');
        },
        legend: {
            labels: {
                generateLabels: function (chart) {
                    var data = chart.data;
                    if (data.labels.length && data.datasets.length) {
                        return data.labels.map(function (label, i) {
                            var meta = chart.getDatasetMeta(0);
                            var ds = data.datasets[0];
                            var arc = meta.data[i];
                            var custom = arc && arc.custom || {};
                            var arcOpts = chart.options.elements.arc;
                            var fill = resolve$3([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i);
                            var stroke = resolve$3([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i);
                            var bw = resolve$3([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i);

                            return {
                                text: label,
                                fillStyle: fill,
                                strokeStyle: stroke,
                                lineWidth: bw,
                                hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

                                // Extra data used for toggling the correct item
                                index: i
                            };
                        });
                    }
                    return [];
                }
            },

            onClick: function (e, legendItem) {
                var index = legendItem.index;
                var chart = this.chart;
                var i, ilen, meta;

                for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
                    meta = chart.getDatasetMeta(i);
                    // toggle visibility of index if exists
                    if (meta.data[index]) {
                        meta.data[index].hidden = !meta.data[index].hidden;
                    }
                }

                chart.update();
            }
        },

        // The percentage of the chart that we cut out of the middle.
        cutoutPercentage: 50,

        // The rotation of the chart, where the first data arc begins.
        rotation: Math.PI * -0.5,

        // The total circumference of the chart.
        circumference: Math.PI * 2.0,

        // Need to override these to give a nice default
        tooltips: {
            callbacks: {
                title: function () {
                    return '';
                },
                label: function (tooltipItem, data) {
                    var dataLabel = data.labels[tooltipItem.index];
                    var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];

                    if (helpers$1.isArray(dataLabel)) {
                        // show value on first line of multiline label
                        // need to clone because we are changing the value
                        dataLabel = dataLabel.slice();
                        dataLabel[0] += value;
                    } else {
                        dataLabel += value;
                    }

                    return dataLabel;
                }
            }
        }
    });

    var controller_doughnut = core_datasetController.extend({

        dataElementType: elements.Arc,

        linkScales: helpers$1.noop,

        // Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
        getRingIndex: function (datasetIndex) {
            var ringIndex = 0;

            for (var j = 0; j < datasetIndex; ++j) {
                if (this.chart.isDatasetVisible(j)) {
                    ++ringIndex;
                }
            }

            return ringIndex;
        },

        update: function (reset) {
            var me = this;
            var chart = me.chart;
            var chartArea = chart.chartArea;
            var opts = chart.options;
            var availableWidth = chartArea.right - chartArea.left;
            var availableHeight = chartArea.bottom - chartArea.top;
            var minSize = Math.min(availableWidth, availableHeight);
            var offset = { x: 0, y: 0 };
            var meta = me.getMeta();
            var arcs = meta.data;
            var cutoutPercentage = opts.cutoutPercentage;
            var circumference = opts.circumference;
            var chartWeight = me._getRingWeight(me.index);
            var i, ilen;

            // If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
            if (circumference < Math.PI * 2.0) {
                var startAngle = opts.rotation % (Math.PI * 2.0);
                startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
                var endAngle = startAngle + circumference;
                var start = { x: Math.cos(startAngle), y: Math.sin(startAngle) };
                var end = { x: Math.cos(endAngle), y: Math.sin(endAngle) };
                var contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
                var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
                var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
                var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
                var cutout = cutoutPercentage / 100.0;
                var min = { x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout)) };
                var max = { x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout)) };
                var size = { width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5 };
                minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
                offset = { x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5 };
            }

            for (i = 0, ilen = arcs.length; i < ilen; ++i) {
                arcs[i]._options = me._resolveElementOptions(arcs[i], i);
            }

            chart.borderWidth = me.getMaxBorderWidth();
            chart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);
            chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);
            chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
            chart.offsetX = offset.x * chart.outerRadius;
            chart.offsetY = offset.y * chart.outerRadius;

            meta.total = me.calculateTotal();

            me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
            me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);

            for (i = 0, ilen = arcs.length; i < ilen; ++i) {
                me.updateElement(arcs[i], i, reset);
            }
        },

        updateElement: function (arc, index, reset) {
            var me = this;
            var chart = me.chart;
            var chartArea = chart.chartArea;
            var opts = chart.options;
            var animationOpts = opts.animation;
            var centerX = (chartArea.left + chartArea.right) / 2;
            var centerY = (chartArea.top + chartArea.bottom) / 2;
            var startAngle = opts.rotation; // non reset case handled later
            var endAngle = opts.rotation; // non reset case handled later
            var dataset = me.getDataset();
            var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));
            var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
            var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
            var options = arc._options || {};

            helpers$1.extend(arc, {
                // Utility
                _datasetIndex: me.index,
                _index: index,

                // Desired view properties
                _model: {
                    backgroundColor: options.backgroundColor,
                    borderColor: options.borderColor,
                    borderWidth: options.borderWidth,
                    borderAlign: options.borderAlign,
                    x: centerX + chart.offsetX,
                    y: centerY + chart.offsetY,
                    startAngle: startAngle,
                    endAngle: endAngle,
                    circumference: circumference,
                    outerRadius: outerRadius,
                    innerRadius: innerRadius,
                    label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
                }
            });

            var model = arc._model;

            // Set correct angles if not resetting
            if (!reset || !animationOpts.animateRotate) {
                if (index === 0) {
                    model.startAngle = opts.rotation;
                } else {
                    model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
                }

                model.endAngle = model.startAngle + model.circumference;
            }

            arc.pivot();
        },

        calculateTotal: function () {
            var dataset = this.getDataset();
            var meta = this.getMeta();
            var total = 0;
            var value;

            helpers$1.each(meta.data, function (element, index) {
                value = dataset.data[index];
                if (!isNaN(value) && !element.hidden) {
                    total += Math.abs(value);
                }
            });

            /* if (total === 0) {
                total = NaN;
            }*/

            return total;
        },

        calculateCircumference: function (value) {
            var total = this.getMeta().total;
            if (total > 0 && !isNaN(value)) {
                return (Math.PI * 2.0) * (Math.abs(value) / total);
            }
            return 0;
        },

        // gets the max border or hover width to properly scale pie charts
        getMaxBorderWidth: function (arcs) {
            var me = this;
            var max = 0;
            var chart = me.chart;
            var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;

            if (!arcs) {
                // Find the outmost visible dataset
                for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
                    if (chart.isDatasetVisible(i)) {
                        meta = chart.getDatasetMeta(i);
                        arcs = meta.data;
                        if (i !== me.index) {
                            controller = meta.controller;
                        }
                        break;
                    }
                }
            }

            if (!arcs) {
                return 0;
            }

            for (i = 0, ilen = arcs.length; i < ilen; ++i) {
                arc = arcs[i];
                options = controller ? controller._resolveElementOptions(arc, i) : arc._options;
                if (options.borderAlign !== 'inner') {
                    borderWidth = options.borderWidth;
                    hoverWidth = options.hoverBorderWidth;

                    max = borderWidth > max ? borderWidth : max;
                    max = hoverWidth > max ? hoverWidth : max;
                }
            }
            return max;
        },

        /**
         * @protected
         */
        setHoverStyle: function (arc) {
            var model = arc._model;
            var options = arc._options;
            var getHoverColor = helpers$1.getHoverColor;

            arc.$previousStyle = {
                backgroundColor: model.backgroundColor,
                borderColor: model.borderColor,
                borderWidth: model.borderWidth,
            };

            model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
            model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor));
            model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth);
        },

        /**
         * @private
         */
        _resolveElementOptions: function (arc, index) {
            var me = this;
            var chart = me.chart;
            var dataset = me.getDataset();
            var custom = arc.custom || {};
            var options = chart.options.elements.arc;
            var values = {};
            var i, ilen, key;

            // Scriptable options
            var context = {
                chart: chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            var keys = [
                'backgroundColor',
                'borderColor',
                'borderWidth',
                'borderAlign',
                'hoverBackgroundColor',
                'hoverBorderColor',
                'hoverBorderWidth',
            ];

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$3([
                    custom[key],
                    dataset[key],
                    options[key]
                ], context, index);
            }

            return values;
        },

        /**
         * Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
         * @private
         */
        _getRingWeightOffset: function (datasetIndex) {
            var ringWeightOffset = 0;

            for (var i = 0; i < datasetIndex; ++i) {
                if (this.chart.isDatasetVisible(i)) {
                    ringWeightOffset += this._getRingWeight(i);
                }
            }

            return ringWeightOffset;
        },

        /**
         * @private
         */
        _getRingWeight: function (dataSetIndex) {
            return Math.max(valueOrDefault$4(this.chart.data.datasets[dataSetIndex].weight, 1), 0);
        },

        /**
         * Returns the sum of all visibile data set weights.  This value can be 0.
         * @private
         */
        _getVisibleDatasetWeightTotal: function () {
            return this._getRingWeightOffset(this.chart.data.datasets.length);
        }
    });

    core_defaults._set('horizontalBar', {
        hover: {
            mode: 'index',
            axis: 'y'
        },

        scales: {
            xAxes: [{
                type: 'linear',
                position: 'bottom'
            }],

            yAxes: [{
                type: 'category',
                position: 'left',
                categoryPercentage: 0.8,
                barPercentage: 0.9,
                offset: true,
                gridLines: {
                    offsetGridLines: true
                }
            }]
        },

        elements: {
            rectangle: {
                borderSkipped: 'left'
            }
        },

        tooltips: {
            mode: 'index',
            axis: 'y'
        }
    });

    var controller_horizontalBar = controller_bar.extend({
        /**
         * @private
         */
        _getValueScaleId: function () {
            return this.getMeta().xAxisID;
        },

        /**
         * @private
         */
        _getIndexScaleId: function () {
            return this.getMeta().yAxisID;
        }
    });

    var valueOrDefault$5 = helpers$1.valueOrDefault;
    var resolve$4 = helpers$1.options.resolve;
    var isPointInArea = helpers$1.canvas._isPointInArea;

    core_defaults._set('line', {
        showLines: true,
        spanGaps: false,

        hover: {
            mode: 'label'
        },

        scales: {
            xAxes: [{
                type: 'category',
                id: 'x-axis-0'
            }],
            yAxes: [{
                type: 'linear',
                id: 'y-axis-0'
            }]
        }
    });

    function lineEnabled(dataset, options) {
        return valueOrDefault$5(dataset.showLine, options.showLines);
    }

    var controller_line = core_datasetController.extend({

        datasetElementType: elements.Line,

        dataElementType: elements.Point,

        update: function (reset) {
            var me = this;
            var meta = me.getMeta();
            var line = meta.dataset;
            var points = meta.data || [];
            var scale = me.getScaleForId(meta.yAxisID);
            var dataset = me.getDataset();
            var showLine = lineEnabled(dataset, me.chart.options);
            var i, ilen;

            // Update Line
            if (showLine) {
                // Compatibility: If the properties are defined with only the old name, use those values
                if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
                    dataset.lineTension = dataset.tension;
                }

                // Utility
                line._scale = scale;
                line._datasetIndex = me.index;
                // Data
                line._children = points;
                // Model
                line._model = me._resolveLineOptions(line);

                line.pivot();
            }

            // Update Points
            for (i = 0, ilen = points.length; i < ilen; ++i) {
                me.updateElement(points[i], i, reset);
            }

            if (showLine && line._model.tension !== 0) {
                me.updateBezierControlPoints();
            }

            // Now pivot the point for animation
            for (i = 0, ilen = points.length; i < ilen; ++i) {
                points[i].pivot();
            }
        },

        updateElement: function (point, index, reset) {
            var me = this;
            var meta = me.getMeta();
            var custom = point.custom || {};
            var dataset = me.getDataset();
            var datasetIndex = me.index;
            var value = dataset.data[index];
            var yScale = me.getScaleForId(meta.yAxisID);
            var xScale = me.getScaleForId(meta.xAxisID);
            var lineModel = meta.dataset._model;
            var x, y;

            var options = me._resolvePointOptions(point, index);

            x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
            y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);

            // Utility
            point._xScale = xScale;
            point._yScale = yScale;
            point._options = options;
            point._datasetIndex = datasetIndex;
            point._index = index;

            // Desired view properties
            point._model = {
                x: x,
                y: y,
                skip: custom.skip || isNaN(x) || isNaN(y),
                // Appearance
                radius: options.radius,
                pointStyle: options.pointStyle,
                rotation: options.rotation,
                backgroundColor: options.backgroundColor,
                borderColor: options.borderColor,
                borderWidth: options.borderWidth,
                tension: valueOrDefault$5(custom.tension, lineModel ? lineModel.tension : 0),
                steppedLine: lineModel ? lineModel.steppedLine : false,
                // Tooltip
                hitRadius: options.hitRadius
            };
        },

        /**
         * @private
         */
        _resolvePointOptions: function (element, index) {
            var me = this;
            var chart = me.chart;
            var dataset = chart.data.datasets[me.index];
            var custom = element.custom || {};
            var options = chart.options.elements.point;
            var values = {};
            var i, ilen, key;

            // Scriptable options
            var context = {
                chart: chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            var ELEMENT_OPTIONS = {
                backgroundColor: 'pointBackgroundColor',
                borderColor: 'pointBorderColor',
                borderWidth: 'pointBorderWidth',
                hitRadius: 'pointHitRadius',
                hoverBackgroundColor: 'pointHoverBackgroundColor',
                hoverBorderColor: 'pointHoverBorderColor',
                hoverBorderWidth: 'pointHoverBorderWidth',
                hoverRadius: 'pointHoverRadius',
                pointStyle: 'pointStyle',
                radius: 'pointRadius',
                rotation: 'pointRotation'
            };
            var keys = Object.keys(ELEMENT_OPTIONS);

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$4([
                    custom[key],
                    dataset[ELEMENT_OPTIONS[key]],
                    dataset[key],
                    options[key]
                ], context, index);
            }

            return values;
        },

        /**
         * @private
         */
        _resolveLineOptions: function (element) {
            var me = this;
            var chart = me.chart;
            var dataset = chart.data.datasets[me.index];
            var custom = element.custom || {};
            var options = chart.options;
            var elementOptions = options.elements.line;
            var values = {};
            var i, ilen, key;

            var keys = [
                'backgroundColor',
                'borderWidth',
                'borderColor',
                'borderCapStyle',
                'borderDash',
                'borderDashOffset',
                'borderJoinStyle',
                'fill',
                'cubicInterpolationMode'
            ];

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$4([
                    custom[key],
                    dataset[key],
                    elementOptions[key]
                ]);
            }

            // The default behavior of lines is to break at null values, according
            // to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
            // This option gives lines the ability to span gaps
            values.spanGaps = valueOrDefault$5(dataset.spanGaps, options.spanGaps);
            values.tension = valueOrDefault$5(dataset.lineTension, elementOptions.tension);
            values.steppedLine = resolve$4([custom.steppedLine, dataset.steppedLine, elementOptions.stepped]);

            return values;
        },

        calculatePointY: function (value, index, datasetIndex) {
            var me = this;
            var chart = me.chart;
            var meta = me.getMeta();
            var yScale = me.getScaleForId(meta.yAxisID);
            var sumPos = 0;
            var sumNeg = 0;
            var i, ds, dsMeta;

            if (yScale.options.stacked) {
                for (i = 0; i < datasetIndex; i++) {
                    ds = chart.data.datasets[i];
                    dsMeta = chart.getDatasetMeta(i);
                    if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {
                        var stackedRightValue = Number(yScale.getRightValue(ds.data[index]));
                        if (stackedRightValue < 0) {
                            sumNeg += stackedRightValue || 0;
                        } else {
                            sumPos += stackedRightValue || 0;
                        }
                    }
                }

                var rightValue = Number(yScale.getRightValue(value));
                if (rightValue < 0) {
                    return yScale.getPixelForValue(sumNeg + rightValue);
                }
                return yScale.getPixelForValue(sumPos + rightValue);
            }

            return yScale.getPixelForValue(value);
        },

        updateBezierControlPoints: function () {
            var me = this;
            var chart = me.chart;
            var meta = me.getMeta();
            var lineModel = meta.dataset._model;
            var area = chart.chartArea;
            var points = meta.data || [];
            var i, ilen, model, controlPoints;

            // Only consider points that are drawn in case the spanGaps option is used
            if (lineModel.spanGaps) {
                points = points.filter(function (pt) {
                    return !pt._model.skip;
                });
            }

            function capControlPoint(pt, min, max) {
                return Math.max(Math.min(pt, max), min);
            }

            if (lineModel.cubicInterpolationMode === 'monotone') {
                helpers$1.splineCurveMonotone(points);
            } else {
                for (i = 0, ilen = points.length; i < ilen; ++i) {
                    model = points[i]._model;
                    controlPoints = helpers$1.splineCurve(
                        helpers$1.previousItem(points, i)._model,
                        model,
                        helpers$1.nextItem(points, i)._model,
                        lineModel.tension
                    );
                    model.controlPointPreviousX = controlPoints.previous.x;
                    model.controlPointPreviousY = controlPoints.previous.y;
                    model.controlPointNextX = controlPoints.next.x;
                    model.controlPointNextY = controlPoints.next.y;
                }
            }

            if (chart.options.elements.line.capBezierPoints) {
                for (i = 0, ilen = points.length; i < ilen; ++i) {
                    model = points[i]._model;
                    if (isPointInArea(model, area)) {
                        if (i > 0 && isPointInArea(points[i - 1]._model, area)) {
                            model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
                            model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
                        }
                        if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {
                            model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
                            model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
                        }
                    }
                }
            }
        },

        draw: function () {
            var me = this;
            var chart = me.chart;
            var meta = me.getMeta();
            var points = meta.data || [];
            var area = chart.chartArea;
            var ilen = points.length;
            var halfBorderWidth;
            var i = 0;

            if (lineEnabled(me.getDataset(), chart.options)) {
                halfBorderWidth = (meta.dataset._model.borderWidth || 0) / 2;

                helpers$1.canvas.clipArea(chart.ctx, {
                    left: area.left,
                    right: area.right,
                    top: area.top - halfBorderWidth,
                    bottom: area.bottom + halfBorderWidth
                });

                meta.dataset.draw();

                helpers$1.canvas.unclipArea(chart.ctx);
            }

            // Draw the points
            for (; i < ilen; ++i) {
                points[i].draw(area);
            }
        },

        /**
         * @protected
         */
        setHoverStyle: function (point) {
            var model = point._model;
            var options = point._options;
            var getHoverColor = helpers$1.getHoverColor;

            point.$previousStyle = {
                backgroundColor: model.backgroundColor,
                borderColor: model.borderColor,
                borderWidth: model.borderWidth,
                radius: model.radius
            };

            model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
            model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor));
            model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth);
            model.radius = valueOrDefault$5(options.hoverRadius, options.radius);
        },
    });

    var resolve$5 = helpers$1.options.resolve;

    core_defaults._set('polarArea', {
        scale: {
            type: 'radialLinear',
            angleLines: {
                display: false
            },
            gridLines: {
                circular: true
            },
            pointLabels: {
                display: false
            },
            ticks: {
                beginAtZero: true
            }
        },

        // Boolean - Whether to animate the rotation of the chart
        animation: {
            animateRotate: true,
            animateScale: true
        },

        startAngle: -0.5 * Math.PI,
        legendCallback: function (chart) {
            var text = [];
            text.push('<ul class="' + chart.id + '-legend">');

            var data = chart.data;
            var datasets = data.datasets;
            var labels = data.labels;

            if (datasets.length) {
                for (var i = 0; i < datasets[0].data.length; ++i) {
                    text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
                    if (labels[i]) {
                        text.push(labels[i]);
                    }
                    text.push('</li>');
                }
            }

            text.push('</ul>');
            return text.join('');
        },
        legend: {
            labels: {
                generateLabels: function (chart) {
                    var data = chart.data;
                    if (data.labels.length && data.datasets.length) {
                        return data.labels.map(function (label, i) {
                            var meta = chart.getDatasetMeta(0);
                            var ds = data.datasets[0];
                            var arc = meta.data[i];
                            var custom = arc.custom || {};
                            var arcOpts = chart.options.elements.arc;
                            var fill = resolve$5([custom.backgroundColor, ds.backgroundColor, arcOpts.backgroundColor], undefined, i);
                            var stroke = resolve$5([custom.borderColor, ds.borderColor, arcOpts.borderColor], undefined, i);
                            var bw = resolve$5([custom.borderWidth, ds.borderWidth, arcOpts.borderWidth], undefined, i);

                            return {
                                text: label,
                                fillStyle: fill,
                                strokeStyle: stroke,
                                lineWidth: bw,
                                hidden: isNaN(ds.data[i]) || meta.data[i].hidden,

                                // Extra data used for toggling the correct item
                                index: i
                            };
                        });
                    }
                    return [];
                }
            },

            onClick: function (e, legendItem) {
                var index = legendItem.index;
                var chart = this.chart;
                var i, ilen, meta;

                for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
                    meta = chart.getDatasetMeta(i);
                    meta.data[index].hidden = !meta.data[index].hidden;
                }

                chart.update();
            }
        },

        // Need to override these to give a nice default
        tooltips: {
            callbacks: {
                title: function () {
                    return '';
                },
                label: function (item, data) {
                    return data.labels[item.index] + ': ' + item.yLabel;
                }
            }
        }
    });

    var controller_polarArea = core_datasetController.extend({

        dataElementType: elements.Arc,

        linkScales: helpers$1.noop,

        update: function (reset) {
            var me = this;
            var dataset = me.getDataset();
            var meta = me.getMeta();
            var start = me.chart.options.startAngle || 0;
            var starts = me._starts = [];
            var angles = me._angles = [];
            var arcs = meta.data;
            var i, ilen, angle;

            me._updateRadius();

            meta.count = me.countVisibleElements();

            for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
                starts[i] = start;
                angle = me._computeAngle(i);
                angles[i] = angle;
                start += angle;
            }

            for (i = 0, ilen = arcs.length; i < ilen; ++i) {
                arcs[i]._options = me._resolveElementOptions(arcs[i], i);
                me.updateElement(arcs[i], i, reset);
            }
        },

        /**
         * @private
         */
        _updateRadius: function () {
            var me = this;
            var chart = me.chart;
            var chartArea = chart.chartArea;
            var opts = chart.options;
            var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);

            chart.outerRadius = Math.max(minSize / 2, 0);
            chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
            chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();

            me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
            me.innerRadius = me.outerRadius - chart.radiusLength;
        },

        updateElement: function (arc, index, reset) {
            var me = this;
            var chart = me.chart;
            var dataset = me.getDataset();
            var opts = chart.options;
            var animationOpts = opts.animation;
            var scale = chart.scale;
            var labels = chart.data.labels;

            var centerX = scale.xCenter;
            var centerY = scale.yCenter;

            // var negHalfPI = -0.5 * Math.PI;
            var datasetStartAngle = opts.startAngle;
            var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
            var startAngle = me._starts[index];
            var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);

            var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
            var options = arc._options || {};

            helpers$1.extend(arc, {
                // Utility
                _datasetIndex: me.index,
                _index: index,
                _scale: scale,

                // Desired view properties
                _model: {
                    backgroundColor: options.backgroundColor,
                    borderColor: options.borderColor,
                    borderWidth: options.borderWidth,
                    borderAlign: options.borderAlign,
                    x: centerX,
                    y: centerY,
                    innerRadius: 0,
                    outerRadius: reset ? resetRadius : distance,
                    startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
                    endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
                    label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index])
                }
            });

            arc.pivot();
        },

        countVisibleElements: function () {
            var dataset = this.getDataset();
            var meta = this.getMeta();
            var count = 0;

            helpers$1.each(meta.data, function (element, index) {
                if (!isNaN(dataset.data[index]) && !element.hidden) {
                    count++;
                }
            });

            return count;
        },

        /**
         * @protected
         */
        setHoverStyle: function (arc) {
            var model = arc._model;
            var options = arc._options;
            var getHoverColor = helpers$1.getHoverColor;
            var valueOrDefault = helpers$1.valueOrDefault;

            arc.$previousStyle = {
                backgroundColor: model.backgroundColor,
                borderColor: model.borderColor,
                borderWidth: model.borderWidth,
            };

            model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
            model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
            model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
        },

        /**
         * @private
         */
        _resolveElementOptions: function (arc, index) {
            var me = this;
            var chart = me.chart;
            var dataset = me.getDataset();
            var custom = arc.custom || {};
            var options = chart.options.elements.arc;
            var values = {};
            var i, ilen, key;

            // Scriptable options
            var context = {
                chart: chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            var keys = [
                'backgroundColor',
                'borderColor',
                'borderWidth',
                'borderAlign',
                'hoverBackgroundColor',
                'hoverBorderColor',
                'hoverBorderWidth',
            ];

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$5([
                    custom[key],
                    dataset[key],
                    options[key]
                ], context, index);
            }

            return values;
        },

        /**
         * @private
         */
        _computeAngle: function (index) {
            var me = this;
            var count = this.getMeta().count;
            var dataset = me.getDataset();
            var meta = me.getMeta();

            if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
                return 0;
            }

            // Scriptable options
            var context = {
                chart: me.chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            return resolve$5([
                me.chart.options.elements.arc.angle,
                (2 * Math.PI) / count
            ], context, index);
        }
    });

    core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut));
    core_defaults._set('pie', {
        cutoutPercentage: 0
    });

    // Pie charts are Doughnut chart with different defaults
    var controller_pie = controller_doughnut;

    var valueOrDefault$6 = helpers$1.valueOrDefault;
    var resolve$6 = helpers$1.options.resolve;

    core_defaults._set('radar', {
        scale: {
            type: 'radialLinear'
        },
        elements: {
            line: {
                tension: 0 // no bezier in radar
            }
        }
    });

    var controller_radar = core_datasetController.extend({

        datasetElementType: elements.Line,

        dataElementType: elements.Point,

        linkScales: helpers$1.noop,

        update: function (reset) {
            var me = this;
            var meta = me.getMeta();
            var line = meta.dataset;
            var points = meta.data || [];
            var scale = me.chart.scale;
            var dataset = me.getDataset();
            var i, ilen;

            // Compatibility: If the properties are defined with only the old name, use those values
            if ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {
                dataset.lineTension = dataset.tension;
            }

            // Utility
            line._scale = scale;
            line._datasetIndex = me.index;
            // Data
            line._children = points;
            line._loop = true;
            // Model
            line._model = me._resolveLineOptions(line);

            line.pivot();

            // Update Points
            for (i = 0, ilen = points.length; i < ilen; ++i) {
                me.updateElement(points[i], i, reset);
            }

            // Update bezier control points
            me.updateBezierControlPoints();

            // Now pivot the point for animation
            for (i = 0, ilen = points.length; i < ilen; ++i) {
                points[i].pivot();
            }
        },

        updateElement: function (point, index, reset) {
            var me = this;
            var custom = point.custom || {};
            var dataset = me.getDataset();
            var scale = me.chart.scale;
            var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
            var options = me._resolvePointOptions(point, index);
            var lineModel = me.getMeta().dataset._model;
            var x = reset ? scale.xCenter : pointPosition.x;
            var y = reset ? scale.yCenter : pointPosition.y;

            // Utility
            point._scale = scale;
            point._options = options;
            point._datasetIndex = me.index;
            point._index = index;

            // Desired view properties
            point._model = {
                x: x, // value not used in dataset scale, but we want a consistent API between scales
                y: y,
                skip: custom.skip || isNaN(x) || isNaN(y),
                // Appearance
                radius: options.radius,
                pointStyle: options.pointStyle,
                rotation: options.rotation,
                backgroundColor: options.backgroundColor,
                borderColor: options.borderColor,
                borderWidth: options.borderWidth,
                tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0),

                // Tooltip
                hitRadius: options.hitRadius
            };
        },

        /**
         * @private
         */
        _resolvePointOptions: function (element, index) {
            var me = this;
            var chart = me.chart;
            var dataset = chart.data.datasets[me.index];
            var custom = element.custom || {};
            var options = chart.options.elements.point;
            var values = {};
            var i, ilen, key;

            // Scriptable options
            var context = {
                chart: chart,
                dataIndex: index,
                dataset: dataset,
                datasetIndex: me.index
            };

            var ELEMENT_OPTIONS = {
                backgroundColor: 'pointBackgroundColor',
                borderColor: 'pointBorderColor',
                borderWidth: 'pointBorderWidth',
                hitRadius: 'pointHitRadius',
                hoverBackgroundColor: 'pointHoverBackgroundColor',
                hoverBorderColor: 'pointHoverBorderColor',
                hoverBorderWidth: 'pointHoverBorderWidth',
                hoverRadius: 'pointHoverRadius',
                pointStyle: 'pointStyle',
                radius: 'pointRadius',
                rotation: 'pointRotation'
            };
            var keys = Object.keys(ELEMENT_OPTIONS);

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$6([
                    custom[key],
                    dataset[ELEMENT_OPTIONS[key]],
                    dataset[key],
                    options[key]
                ], context, index);
            }

            return values;
        },

        /**
         * @private
         */
        _resolveLineOptions: function (element) {
            var me = this;
            var chart = me.chart;
            var dataset = chart.data.datasets[me.index];
            var custom = element.custom || {};
            var options = chart.options.elements.line;
            var values = {};
            var i, ilen, key;

            var keys = [
                'backgroundColor',
                'borderWidth',
                'borderColor',
                'borderCapStyle',
                'borderDash',
                'borderDashOffset',
                'borderJoinStyle',
                'fill'
            ];

            for (i = 0, ilen = keys.length; i < ilen; ++i) {
                key = keys[i];
                values[key] = resolve$6([
                    custom[key],
                    dataset[key],
                    options[key]
                ]);
            }

            values.tension = valueOrDefault$6(dataset.lineTension, options.tension);

            return values;
        },

        updateBezierControlPoints: function () {
            var me = this;
            var meta = me.getMeta();
            var area = me.chart.chartArea;
            var points = meta.data || [];
            var i, ilen, model, controlPoints;

            function capControlPoint(pt, min, max) {
                return Math.max(Math.min(pt, max), min);
            }

            for (i = 0, ilen = points.length; i < ilen; ++i) {
                model = points[i]._model;
                controlPoints = helpers$1.splineCurve(
                    helpers$1.previousItem(points, i, true)._model,
                    model,
                    helpers$1.nextItem(points, i, true)._model,
                    model.tension
                );

                // Prevent the bezier going outside of the bounds of the graph
                model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
                model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
                model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
                model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
            }
        },

        setHoverStyle: function (point) {
            var model = point._model;
            var options = point._options;
            var getHoverColor = helpers$1.getHoverColor;

            point.$previousStyle = {
                backgroundColor: model.backgroundColor,
                borderColor: model.borderColor,
                borderWidth: model.borderWidth,
                radius: model.radius
            };

            model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
            model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));
            model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);
            model.radius = valueOrDefault$6(options.hoverRadius, options.radius);
        }
    });

    core_defaults._set('scatter', {
        hover: {
            mode: 'single'
        },

        scales: {
            xAxes: [{
                id: 'x-axis-1',    // need an ID so datasets can reference the scale
                type: 'linear',    // scatter should not use a category axis
                position: 'bottom'
            }],
            yAxes: [{
                id: 'y-axis-1',
                type: 'linear',
                position: 'left'
            }]
        },

        showLines: false,

        tooltips: {
            callbacks: {
                title: function () {
                    return '';     // doesn't make sense for scatter since data are formatted as a point
                },
                label: function (item) {
                    return '(' + item.xLabel + ', ' + item.yLabel + ')';
                }
            }
        }
    });

    // Scatter charts use line controllers
    var controller_scatter = controller_line;

    // NOTE export a map in which the key represents the controller type, not
    // the class, and so must be CamelCase in order to be correctly retrieved
    // by the controller in core.controller.js (`controllers[meta.type]`).

    var controllers = {
        bar: controller_bar,
        bubble: controller_bubble,
        doughnut: controller_doughnut,
        horizontalBar: controller_horizontalBar,
        line: controller_line,
        polarArea: controller_polarArea,
        pie: controller_pie,
        radar: controller_radar,
        scatter: controller_scatter
    };

    /**
     * Helper function to get relative position for an event
     * @param {Event|IEvent} event - The event to get the position for
     * @param {Chart} chart - The chart
     * @returns {object} the event position
     */
    function getRelativePosition(e, chart) {
        if (e.native) {
            return {
                x: e.x,
                y: e.y
            };
        }

        return helpers$1.getRelativePosition(e, chart);
    }

    /**
     * Helper function to traverse all of the visible elements in the chart
     * @param {Chart} chart - the chart
     * @param {function} handler - the callback to execute for each visible item
     */
    function parseVisibleItems(chart, handler) {
        var datasets = chart.data.datasets;
        var meta, i, j, ilen, jlen;

        for (i = 0, ilen = datasets.length; i < ilen; ++i) {
            if (!chart.isDatasetVisible(i)) {
                continue;
            }

            meta = chart.getDatasetMeta(i);
            for (j = 0, jlen = meta.data.length; j < jlen; ++j) {
                var element = meta.data[j];
                if (!element._view.skip) {
                    handler(element);
                }
            }
        }
    }

    /**
     * Helper function to get the items that intersect the event position
     * @param {ChartElement[]} items - elements to filter
     * @param {object} position - the point to be nearest to
     * @return {ChartElement[]} the nearest items
     */
    function getIntersectItems(chart, position) {
        var elements = [];

        parseVisibleItems(chart, function (element) {
            if (element.inRange(position.x, position.y)) {
                elements.push(element);
            }
        });

        return elements;
    }

    /**
     * Helper function to get the items nearest to the event position considering all visible items in teh chart
     * @param {Chart} chart - the chart to look at elements from
     * @param {object} position - the point to be nearest to
     * @param {boolean} intersect - if true, only consider items that intersect the position
     * @param {function} distanceMetric - function to provide the distance between points
     * @return {ChartElement[]} the nearest items
     */
    function getNearestItems(chart, position, intersect, distanceMetric) {
        var minDistance = Number.POSITIVE_INFINITY;
        var nearestItems = [];

        parseVisibleItems(chart, function (element) {
            if (intersect && !element.inRange(position.x, position.y)) {
                return;
            }

            var center = element.getCenterPoint();
            var distance = distanceMetric(position, center);
            if (distance < minDistance) {
                nearestItems = [element];
                minDistance = distance;
            } else if (distance === minDistance) {
                // Can have multiple items at the same distance in which case we sort by size
                nearestItems.push(element);
            }
        });

        return nearestItems;
    }

    /**
     * Get a distance metric function for two points based on the
     * axis mode setting
     * @param {string} axis - the axis mode. x|y|xy
     */
    function getDistanceMetricForAxis(axis) {
        var useX = axis.indexOf('x') !== -1;
        var useY = axis.indexOf('y') !== -1;

        return function (pt1, pt2) {
            var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
            var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
            return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
        };
    }

    function indexMode(chart, e, options) {
        var position = getRelativePosition(e, chart);
        // Default axis for index mode is 'x' to match old behaviour
        options.axis = options.axis || 'x';
        var distanceMetric = getDistanceMetricForAxis(options.axis);
        var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
        var elements = [];

        if (!items.length) {
            return [];
        }

        chart.data.datasets.forEach(function (dataset, datasetIndex) {
            if (chart.isDatasetVisible(datasetIndex)) {
                var meta = chart.getDatasetMeta(datasetIndex);
                var element = meta.data[items[0]._index];

                // don't count items that are skipped (null data)
                if (element && !element._view.skip) {
                    elements.push(element);
                }
            }
        });

        return elements;
    }

    /**
     * @interface IInteractionOptions
     */
    /**
     * If true, only consider items that intersect the point
     * @name IInterfaceOptions#boolean
     * @type Boolean
     */

    /**
     * Contains interaction related functions
     * @namespace Chart.Interaction
     */
    var core_interaction = {
        // Helper function for different modes
        modes: {
            single: function (chart, e) {
                var position = getRelativePosition(e, chart);
                var elements = [];

                parseVisibleItems(chart, function (element) {
                    if (element.inRange(position.x, position.y)) {
                        elements.push(element);
                        return elements;
                    }
                });

                return elements.slice(0, 1);
            },

            /**
             * @function Chart.Interaction.modes.label
             * @deprecated since version 2.4.0
             * @todo remove at version 3
             * @private
             */
            label: indexMode,

            /**
             * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
             * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
             * @function Chart.Interaction.modes.index
             * @since v2.4.0
             * @param {Chart} chart - the chart we are returning items from
             * @param {Event} e - the event we are find things at
             * @param {IInteractionOptions} options - options to use during interaction
             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
             */
            index: indexMode,

            /**
             * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
             * If the options.intersect is false, we find the nearest item and return the items in that dataset
             * @function Chart.Interaction.modes.dataset
             * @param {Chart} chart - the chart we are returning items from
             * @param {Event} e - the event we are find things at
             * @param {IInteractionOptions} options - options to use during interaction
             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
             */
            dataset: function (chart, e, options) {
                var position = getRelativePosition(e, chart);
                options.axis = options.axis || 'xy';
                var distanceMetric = getDistanceMetricForAxis(options.axis);
                var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);

                if (items.length > 0) {
                    items = chart.getDatasetMeta(items[0]._datasetIndex).data;
                }

                return items;
            },

            /**
             * @function Chart.Interaction.modes.x-axis
             * @deprecated since version 2.4.0. Use index mode and intersect == true
             * @todo remove at version 3
             * @private
             */
            'x-axis': function (chart, e) {
                return indexMode(chart, e, { intersect: false });
            },

            /**
             * Point mode returns all elements that hit test based on the event position
             * of the event
             * @function Chart.Interaction.modes.intersect
             * @param {Chart} chart - the chart we are returning items from
             * @param {Event} e - the event we are find things at
             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
             */
            point: function (chart, e) {
                var position = getRelativePosition(e, chart);
                return getIntersectItems(chart, position);
            },

            /**
             * nearest mode returns the element closest to the point
             * @function Chart.Interaction.modes.intersect
             * @param {Chart} chart - the chart we are returning items from
             * @param {Event} e - the event we are find things at
             * @param {IInteractionOptions} options - options to use
             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
             */
            nearest: function (chart, e, options) {
                var position = getRelativePosition(e, chart);
                options.axis = options.axis || 'xy';
                var distanceMetric = getDistanceMetricForAxis(options.axis);
                return getNearestItems(chart, position, options.intersect, distanceMetric);
            },

            /**
             * x mode returns the elements that hit-test at the current x coordinate
             * @function Chart.Interaction.modes.x
             * @param {Chart} chart - the chart we are returning items from
             * @param {Event} e - the event we are find things at
             * @param {IInteractionOptions} options - options to use
             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
             */
            x: function (chart, e, options) {
                var position = getRelativePosition(e, chart);
                var items = [];
                var intersectsItem = false;

                parseVisibleItems(chart, function (element) {
                    if (element.inXRange(position.x)) {
                        items.push(element);
                    }

                    if (element.inRange(position.x, position.y)) {
                        intersectsItem = true;
                    }
                });

                // If we want to trigger on an intersect and we don't have any items
                // that intersect the position, return nothing
                if (options.intersect && !intersectsItem) {
                    items = [];
                }
                return items;
            },

            /**
             * y mode returns the elements that hit-test at the current y coordinate
             * @function Chart.Interaction.modes.y
             * @param {Chart} chart - the chart we are returning items from
             * @param {Event} e - the event we are find things at
             * @param {IInteractionOptions} options - options to use
             * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
             */
            y: function (chart, e, options) {
                var position = getRelativePosition(e, chart);
                var items = [];
                var intersectsItem = false;

                parseVisibleItems(chart, function (element) {
                    if (element.inYRange(position.y)) {
                        items.push(element);
                    }

                    if (element.inRange(position.x, position.y)) {
                        intersectsItem = true;
                    }
                });

                // If we want to trigger on an intersect and we don't have any items
                // that intersect the position, return nothing
                if (options.intersect && !intersectsItem) {
                    items = [];
                }
                return items;
            }
        }
    };

    function filterByPosition(array, position) {
        return helpers$1.where(array, function (v) {
            return v.position === position;
        });
    }

    function sortByWeight(array, reverse) {
        array.forEach(function (v, i) {
            v._tmpIndex_ = i;
            return v;
        });
        array.sort(function (a, b) {
            var v0 = reverse ? b : a;
            var v1 = reverse ? a : b;
            return v0.weight === v1.weight ?
                v0._tmpIndex_ - v1._tmpIndex_ :
                v0.weight - v1.weight;
        });
        array.forEach(function (v) {
            delete v._tmpIndex_;
        });
    }

    function findMaxPadding(boxes) {
        var top = 0;
        var left = 0;
        var bottom = 0;
        var right = 0;
        helpers$1.each(boxes, function (box) {
            if (box.getPadding) {
                var boxPadding = box.getPadding();
                top = Math.max(top, boxPadding.top);
                left = Math.max(left, boxPadding.left);
                bottom = Math.max(bottom, boxPadding.bottom);
                right = Math.max(right, boxPadding.right);
            }
        });
        return {
            top: top,
            left: left,
            bottom: bottom,
            right: right
        };
    }

    function addSizeByPosition(boxes, size) {
        helpers$1.each(boxes, function (box) {
            size[box.position] += box.isHorizontal() ? box.height : box.width;
        });
    }

    core_defaults._set('global', {
        layout: {
            padding: {
                top: 0,
                right: 0,
                bottom: 0,
                left: 0
            }
        }
    });

    /**
     * @interface ILayoutItem
     * @prop {string} position - The position of the item in the chart layout. Possible values are
     * 'left', 'top', 'right', 'bottom', and 'chartArea'
     * @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area
     * @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
     * @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
     * @prop {function} update - Takes two parameters: width and height. Returns size of item
     * @prop {function} getPadding -  Returns an object with padding on the edges
     * @prop {number} width - Width of item. Must be valid after update()
     * @prop {number} height - Height of item. Must be valid after update()
     * @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update
     * @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update
     * @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update
     * @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
     */

    // The layout service is very self explanatory.  It's responsible for the layout within a chart.
    // Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
    // It is this service's responsibility of carrying out that layout.
    var core_layouts = {
        defaults: {},

        /**
         * Register a box to a chart.
         * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
         * @param {Chart} chart - the chart to use
         * @param {ILayoutItem} item - the item to add to be layed out
         */
        addBox: function (chart, item) {
            if (!chart.boxes) {
                chart.boxes = [];
            }

            // initialize item with default values
            item.fullWidth = item.fullWidth || false;
            item.position = item.position || 'top';
            item.weight = item.weight || 0;

            chart.boxes.push(item);
        },

        /**
         * Remove a layoutItem from a chart
         * @param {Chart} chart - the chart to remove the box from
         * @param {ILayoutItem} layoutItem - the item to remove from the layout
         */
        removeBox: function (chart, layoutItem) {
            var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
            if (index !== -1) {
                chart.boxes.splice(index, 1);
            }
        },

        /**
         * Sets (or updates) options on the given `item`.
         * @param {Chart} chart - the chart in which the item lives (or will be added to)
         * @param {ILayoutItem} item - the item to configure with the given options
         * @param {object} options - the new item options.
         */
        configure: function (chart, item, options) {
            var props = ['fullWidth', 'position', 'weight'];
            var ilen = props.length;
            var i = 0;
            var prop;

            for (; i < ilen; ++i) {
                prop = props[i];
                if (options.hasOwnProperty(prop)) {
                    item[prop] = options[prop];
                }
            }
        },

        /**
         * Fits boxes of the given chart into the given size by having each box measure itself
         * then running a fitting algorithm
         * @param {Chart} chart - the chart
         * @param {number} width - the width to fit into
         * @param {number} height - the height to fit into
         */
        update: function (chart, width, height) {
            if (!chart) {
                return;
            }

            var layoutOptions = chart.options.layout || {};
            var padding = helpers$1.options.toPadding(layoutOptions.padding);
            var leftPadding = padding.left;
            var rightPadding = padding.right;
            var topPadding = padding.top;
            var bottomPadding = padding.bottom;

            var leftBoxes = filterByPosition(chart.boxes, 'left');
            var rightBoxes = filterByPosition(chart.boxes, 'right');
            var topBoxes = filterByPosition(chart.boxes, 'top');
            var bottomBoxes = filterByPosition(chart.boxes, 'bottom');
            var chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');

            // Sort boxes by weight. A higher weight is further away from the chart area
            sortByWeight(leftBoxes, true);
            sortByWeight(rightBoxes, false);
            sortByWeight(topBoxes, true);
            sortByWeight(bottomBoxes, false);

            var verticalBoxes = leftBoxes.concat(rightBoxes);
            var horizontalBoxes = topBoxes.concat(bottomBoxes);
            var outerBoxes = verticalBoxes.concat(horizontalBoxes);

            // Essentially we now have any number of boxes on each of the 4 sides.
            // Our canvas looks like the following.
            // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
            // B1 is the bottom axis
            // There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
            // These locations are single-box locations only, when trying to register a chartArea location that is already taken,
            // an error will be thrown.
            //
            // |----------------------------------------------------|
            // |                  T1 (Full Width)                   |
            // |----------------------------------------------------|
            // |    |    |                 T2                  |    |
            // |    |----|-------------------------------------|----|
            // |    |    | C1 |                           | C2 |    |
            // |    |    |----|                           |----|    |
            // |    |    |                                     |    |
            // | L1 | L2 |           ChartArea (C0)            | R1 |
            // |    |    |                                     |    |
            // |    |    |----|                           |----|    |
            // |    |    | C3 |                           | C4 |    |
            // |    |----|-------------------------------------|----|
            // |    |    |                 B1                  |    |
            // |----------------------------------------------------|
            // |                  B2 (Full Width)                   |
            // |----------------------------------------------------|
            //
            // What we do to find the best sizing, we do the following
            // 1. Determine the minimum size of the chart area.
            // 2. Split the remaining width equally between each vertical axis
            // 3. Split the remaining height equally between each horizontal axis
            // 4. Give each layout the maximum size it can be. The layout will return it's minimum size
            // 5. Adjust the sizes of each axis based on it's minimum reported size.
            // 6. Refit each axis
            // 7. Position each axis in the final location
            // 8. Tell the chart the final location of the chart area
            // 9. Tell any axes that overlay the chart area the positions of the chart area

            // Step 1
            var chartWidth = width - leftPadding - rightPadding;
            var chartHeight = height - topPadding - bottomPadding;
            var chartAreaWidth = chartWidth / 2; // min 50%

            // Step 2
            var verticalBoxWidth = (width - chartAreaWidth) / verticalBoxes.length;

            // Step 3
            // TODO re-limit horizontal axis height (this limit has affected only padding calculation since PR 1837)
            // var horizontalBoxHeight = (height - chartAreaHeight) / horizontalBoxes.length;

            // Step 4
            var maxChartAreaWidth = chartWidth;
            var maxChartAreaHeight = chartHeight;
            var outerBoxSizes = { top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding };
            var minBoxSizes = [];
            var maxPadding;

            function getMinimumBoxSize(box) {
                var minSize;
                var isHorizontal = box.isHorizontal();

                if (isHorizontal) {
                    minSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2);
                    maxChartAreaHeight -= minSize.height;
                } else {
                    minSize = box.update(verticalBoxWidth, maxChartAreaHeight);
                    maxChartAreaWidth -= minSize.width;
                }

                minBoxSizes.push({
                    horizontal: isHorizontal,
                    width: minSize.width,
                    box: box,
                });
            }

            helpers$1.each(outerBoxes, getMinimumBoxSize);

            // If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)
            maxPadding = findMaxPadding(outerBoxes);

            // At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could
            // be if the axes are drawn at their minimum sizes.
            // Steps 5 & 6

            // Function to fit a box
            function fitBox(box) {
                var minBoxSize = helpers$1.findNextWhere(minBoxSizes, function (minBox) {
                    return minBox.box === box;
                });

                if (minBoxSize) {
                    if (minBoxSize.horizontal) {
                        var scaleMargin = {
                            left: Math.max(outerBoxSizes.left, maxPadding.left),
                            right: Math.max(outerBoxSizes.right, maxPadding.right),
                            top: 0,
                            bottom: 0
                        };

                        // Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends
                        // on the margin. Sometimes they need to increase in size slightly
                        box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);
                    } else {
                        box.update(minBoxSize.width, maxChartAreaHeight);
                    }
                }
            }

            // Update, and calculate the left and right margins for the horizontal boxes
            helpers$1.each(verticalBoxes, fitBox);
            addSizeByPosition(verticalBoxes, outerBoxSizes);

            // Set the Left and Right margins for the horizontal boxes
            helpers$1.each(horizontalBoxes, fitBox);
            addSizeByPosition(horizontalBoxes, outerBoxSizes);

            function finalFitVerticalBox(box) {
                var minBoxSize = helpers$1.findNextWhere(minBoxSizes, function (minSize) {
                    return minSize.box === box;
                });

                var scaleMargin = {
                    left: 0,
                    right: 0,
                    top: outerBoxSizes.top,
                    bottom: outerBoxSizes.bottom
                };

                if (minBoxSize) {
                    box.update(minBoxSize.width, maxChartAreaHeight, scaleMargin);
                }
            }

            // Let the left layout know the final margin
            helpers$1.each(verticalBoxes, finalFitVerticalBox);

            // Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)
            outerBoxSizes = { top: topPadding, left: leftPadding, bottom: bottomPadding, right: rightPadding };
            addSizeByPosition(outerBoxes, outerBoxSizes);

            // We may be adding some padding to account for rotated x axis labels
            var leftPaddingAddition = Math.max(maxPadding.left - outerBoxSizes.left, 0);
            outerBoxSizes.left += leftPaddingAddition;
            outerBoxSizes.right += Math.max(maxPadding.right - outerBoxSizes.right, 0);

            var topPaddingAddition = Math.max(maxPadding.top - outerBoxSizes.top, 0);
            outerBoxSizes.top += topPaddingAddition;
            outerBoxSizes.bottom += Math.max(maxPadding.bottom - outerBoxSizes.bottom, 0);

            // Figure out if our chart area changed. This would occur if the dataset layout label rotation
            // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
            // without calling `fit` again
            var newMaxChartAreaHeight = height - outerBoxSizes.top - outerBoxSizes.bottom;
            var newMaxChartAreaWidth = width - outerBoxSizes.left - outerBoxSizes.right;

            if (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {
                helpers$1.each(verticalBoxes, function (box) {
                    box.height = newMaxChartAreaHeight;
                });

                helpers$1.each(horizontalBoxes, function (box) {
                    if (!box.fullWidth) {
                        box.width = newMaxChartAreaWidth;
                    }
                });

                maxChartAreaHeight = newMaxChartAreaHeight;
                maxChartAreaWidth = newMaxChartAreaWidth;
            }

            // Step 7 - Position the boxes
            var left = leftPadding + leftPaddingAddition;
            var top = topPadding + topPaddingAddition;

            function placeBox(box) {
                if (box.isHorizontal()) {
                    box.left = box.fullWidth ? leftPadding : outerBoxSizes.left;
                    box.right = box.fullWidth ? width - rightPadding : outerBoxSizes.left + maxChartAreaWidth;
                    box.top = top;
                    box.bottom = top + box.height;

                    // Move to next point
                    top = box.bottom;

                } else {

                    box.left = left;
                    box.right = left + box.width;
                    box.top = outerBoxSizes.top;
                    box.bottom = outerBoxSizes.top + maxChartAreaHeight;

                    // Move to next point
                    left = box.right;
                }
            }

            helpers$1.each(leftBoxes.concat(topBoxes), placeBox);

            // Account for chart width and height
            left += maxChartAreaWidth;
            top += maxChartAreaHeight;

            helpers$1.each(rightBoxes, placeBox);
            helpers$1.each(bottomBoxes, placeBox);

            // Step 8
            chart.chartArea = {
                left: outerBoxSizes.left,
                top: outerBoxSizes.top,
                right: outerBoxSizes.left + maxChartAreaWidth,
                bottom: outerBoxSizes.top + maxChartAreaHeight
            };

            // Step 9
            helpers$1.each(chartAreaBoxes, function (box) {
                box.left = chart.chartArea.left;
                box.top = chart.chartArea.top;
                box.right = chart.chartArea.right;
                box.bottom = chart.chartArea.bottom;

                box.update(maxChartAreaWidth, maxChartAreaHeight);
            });
        }
    };

    /**
     * Platform fallback implementation (minimal).
     * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
     */

    var platform_basic = {
        acquireContext: function (item) {
            if (item && item.canvas) {
                // Support for any object associated to a canvas (including a context2d)
                item = item.canvas;
            }

            return item && item.getContext('2d') || null;
        }
    };

    var platform_dom = "/*\n * DOM element rendering detection\n * https://davidwalsh.name/detect-node-insertion\n */\n@keyframes chartjs-render-animation {\n\tfrom { opacity: 0.99; }\n\tto { opacity: 1; }\n}\n\n.chartjs-render-monitor {\n\tanimation: chartjs-render-animation 0.001s;\n}\n\n/*\n * DOM element resizing detection\n * https://github.com/marcj/css-element-queries\n */\n.chartjs-size-monitor,\n.chartjs-size-monitor-expand,\n.chartjs-size-monitor-shrink {\n\tposition: absolute;\n\tdirection: ltr;\n\tleft: 0;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\toverflow: hidden;\n\tpointer-events: none;\n\tvisibility: hidden;\n\tz-index: -1;\n}\n\n.chartjs-size-monitor-expand > div {\n\tposition: absolute;\n\twidth: 1000000px;\n\theight: 1000000px;\n\tleft: 0;\n\ttop: 0;\n}\n\n.chartjs-size-monitor-shrink > div {\n\tposition: absolute;\n\twidth: 200%;\n\theight: 200%;\n\tleft: 0;\n\ttop: 0;\n}\n";

    var platform_dom$1 = /*#__PURE__*/Object.freeze({
        default: platform_dom
    });

    function getCjsExportFromNamespace(n) {
        return n && n.default || n;
    }

    var stylesheet = getCjsExportFromNamespace(platform_dom$1);

    var EXPANDO_KEY = '$chartjs';
    var CSS_PREFIX = 'chartjs-';
    var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor';
    var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
    var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
    var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];

    /**
     * DOM event types -> Chart.js event types.
     * Note: only events with different types are mapped.
     * @see https://developer.mozilla.org/en-US/docs/Web/Events
     */
    var EVENT_TYPES = {
        touchstart: 'mousedown',
        touchmove: 'mousemove',
        touchend: 'mouseup',
        pointerenter: 'mouseenter',
        pointerdown: 'mousedown',
        pointermove: 'mousemove',
        pointerup: 'mouseup',
        pointerleave: 'mouseout',
        pointerout: 'mouseout'
    };

    /**
     * The "used" size is the final value of a dimension property after all calculations have
     * been performed. This method uses the computed style of `element` but returns undefined
     * if the computed style is not expressed in pixels. That can happen in some cases where
     * `element` has a size relative to its parent and this last one is not yet displayed,
     * for example because of `display: none` on a parent node.
     * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
     * @returns {number} Size in pixels or undefined if unknown.
     */
    function readUsedSize(element, property) {
        var value = helpers$1.getStyle(element, property);
        var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
        return matches ? Number(matches[1]) : undefined;
    }

    /**
     * Initializes the canvas style and render size without modifying the canvas display size,
     * since responsiveness is handled by the controller.resize() method. The config is used
     * to determine the aspect ratio to apply in case no explicit height has been specified.
     */
    function initCanvas(canvas, config) {
        var style = canvas.style;

        // NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
        // returns null or '' if no explicit value has been set to the canvas attribute.
        var renderHeight = canvas.getAttribute('height');
        var renderWidth = canvas.getAttribute('width');

        // Chart.js modifies some canvas values that we want to restore on destroy
        canvas[EXPANDO_KEY] = {
            initial: {
                height: renderHeight,
                width: renderWidth,
                style: {
                    display: style.display,
                    height: style.height,
                    width: style.width
                }
            }
        };

        // Force canvas to display as block to avoid extra space caused by inline
        // elements, which would interfere with the responsive resize process.
        // https://github.com/chartjs/Chart.js/issues/2538
        style.display = style.display || 'block';

        if (renderWidth === null || renderWidth === '') {
            var displayWidth = readUsedSize(canvas, 'width');
            if (displayWidth !== undefined) {
                canvas.width = displayWidth;
            }
        }

        if (renderHeight === null || renderHeight === '') {
            if (canvas.style.height === '') {
                // If no explicit render height and style height, let's apply the aspect ratio,
                // which one can be specified by the user but also by charts as default option
                // (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
                canvas.height = canvas.width / (config.options.aspectRatio || 2);
            } else {
                var displayHeight = readUsedSize(canvas, 'height');
                if (displayWidth !== undefined) {
                    canvas.height = displayHeight;
                }
            }
        }

        return canvas;
    }

    /**
     * Detects support for options object argument in addEventListener.
     * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
     * @private
     */
    var supportsEventListenerOptions = (function () {
        var supports = false;
        try {
            var options = Object.defineProperty({}, 'passive', {
                // eslint-disable-next-line getter-return
                get: function () {
                    supports = true;
                }
            });
            window.addEventListener('e', null, options);
        } catch (e) {
            // continue regardless of error
        }
        return supports;
    }());

    // Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
    // https://github.com/chartjs/Chart.js/issues/4287
    var eventListenerOptions = supportsEventListenerOptions ? { passive: true } : false;

    function addListener(node, type, listener) {
        node.addEventListener(type, listener, eventListenerOptions);
    }

    function removeListener(node, type, listener) {
        node.removeEventListener(type, listener, eventListenerOptions);
    }

    function createEvent(type, chart, x, y, nativeEvent) {
        return {
            type: type,
            chart: chart,
            native: nativeEvent || null,
            x: x !== undefined ? x : null,
            y: y !== undefined ? y : null,
        };
    }

    function fromNativeEvent(event, chart) {
        var type = EVENT_TYPES[event.type] || event.type;
        var pos = helpers$1.getRelativePosition(event, chart);
        return createEvent(type, chart, pos.x, pos.y, event);
    }

    function throttled(fn, thisArg) {
        var ticking = false;
        var args = [];

        return function () {
            args = Array.prototype.slice.call(arguments);
            thisArg = thisArg || this;

            if (!ticking) {
                ticking = true;
                helpers$1.requestAnimFrame.call(window, function () {
                    ticking = false;
                    fn.apply(thisArg, args);
                });
            }
        };
    }

    function createDiv(cls) {
        var el = document.createElement('div');
        el.className = cls || '';
        return el;
    }

    // Implementation based on https://github.com/marcj/css-element-queries
    function createResizer(handler) {
        var maxSize = 1000000;

        // NOTE(SB) Don't use innerHTML because it could be considered unsafe.
        // https://github.com/chartjs/Chart.js/issues/5902
        var resizer = createDiv(CSS_SIZE_MONITOR);
        var expand = createDiv(CSS_SIZE_MONITOR + '-expand');
        var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink');

        expand.appendChild(createDiv());
        shrink.appendChild(createDiv());

        resizer.appendChild(expand);
        resizer.appendChild(shrink);
        resizer._reset = function () {
            expand.scrollLeft = maxSize;
            expand.scrollTop = maxSize;
            shrink.scrollLeft = maxSize;
            shrink.scrollTop = maxSize;
        };

        var onScroll = function () {
            resizer._reset();
            handler();
        };

        addListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
        addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));

        return resizer;
    }

    // https://davidwalsh.name/detect-node-insertion
    function watchForRender(node, handler) {
        var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
        var proxy = expando.renderProxy = function (e) {
            if (e.animationName === CSS_RENDER_ANIMATION) {
                handler();
            }
        };

        helpers$1.each(ANIMATION_START_EVENTS, function (type) {
            addListener(node, type, proxy);
        });

        // #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
        // is removed then added back immediately (same animation frame?). Accessing the
        // `offsetParent` property will force a reflow and re-evaluate the CSS animation.
        // https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
        // https://github.com/chartjs/Chart.js/issues/4737
        expando.reflow = !!node.offsetParent;

        node.classList.add(CSS_RENDER_MONITOR);
    }

    function unwatchForRender(node) {
        var expando = node[EXPANDO_KEY] || {};
        var proxy = expando.renderProxy;

        if (proxy) {
            helpers$1.each(ANIMATION_START_EVENTS, function (type) {
                removeListener(node, type, proxy);
            });

            delete expando.renderProxy;
        }

        node.classList.remove(CSS_RENDER_MONITOR);
    }

    function addResizeListener(node, listener, chart) {
        var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});

        // Let's keep track of this added resizer and thus avoid DOM query when removing it.
        var resizer = expando.resizer = createResizer(throttled(function () {
            if (expando.resizer) {
                var container = chart.options.maintainAspectRatio && node.parentNode;
                var w = container ? container.clientWidth : 0;
                listener(createEvent('resize', chart));
                if (container && container.clientWidth < w && chart.canvas) {
                    // If the container size shrank during chart resize, let's assume
                    // scrollbar appeared. So we resize again with the scrollbar visible -
                    // effectively making chart smaller and the scrollbar hidden again.
                    // Because we are inside `throttled`, and currently `ticking`, scroll
                    // events are ignored during this whole 2 resize process.
                    // If we assumed wrong and something else happened, we are resizing
                    // twice in a frame (potential performance issue)
                    listener(createEvent('resize', chart));
                }
            }
        }));

        // The resizer needs to be attached to the node parent, so we first need to be
        // sure that `node` is attached to the DOM before injecting the resizer element.
        watchForRender(node, function () {
            if (expando.resizer) {
                var container = node.parentNode;
                if (container && container !== resizer.parentNode) {
                    container.insertBefore(resizer, container.firstChild);
                }

                // The container size might have changed, let's reset the resizer state.
                resizer._reset();
            }
        });
    }

    function removeResizeListener(node) {
        var expando = node[EXPANDO_KEY] || {};
        var resizer = expando.resizer;

        delete expando.resizer;
        unwatchForRender(node);

        if (resizer && resizer.parentNode) {
            resizer.parentNode.removeChild(resizer);
        }
    }

    function injectCSS(platform, css) {
        // https://stackoverflow.com/q/3922139
        var style = platform._style || document.createElement('style');
        if (!platform._style) {
            platform._style = style;
            css = '/* Chart.js */\n' + css;
            style.setAttribute('type', 'text/css');
            document.getElementsByTagName('head')[0].appendChild(style);
        }

        style.appendChild(document.createTextNode(css));
    }

    var platform_dom$2 = {
        /**
         * When `true`, prevents the automatic injection of the stylesheet required to
         * correctly detect when the chart is added to the DOM and then resized. This
         * switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
         * to be manually imported to make this library compatible with any CSP.
         * See https://github.com/chartjs/Chart.js/issues/5208
         */
        disableCSSInjection: false,

        /**
         * This property holds whether this platform is enabled for the current environment.
         * Currently used by platform.js to select the proper implementation.
         * @private
         */
        _enabled: typeof window !== 'undefined' && typeof document !== 'undefined',

        /**
         * @private
         */
        _ensureLoaded: function () {
            if (this._loaded) {
                return;
            }

            this._loaded = true;

            // https://github.com/chartjs/Chart.js/issues/5208
            if (!this.disableCSSInjection) {
                injectCSS(this, stylesheet);
            }
        },

        acquireContext: function (item, config) {
            if (typeof item === 'string') {
                item = document.getElementById(item);
            } else if (item.length) {
                // Support for array based queries (such as jQuery)
                item = item[0];
            }

            if (item && item.canvas) {
                // Support for any object associated to a canvas (including a context2d)
                item = item.canvas;
            }

            // To prevent canvas fingerprinting, some add-ons undefine the getContext
            // method, for example: https://github.com/kkapsner/CanvasBlocker
            // https://github.com/chartjs/Chart.js/issues/2807
            var context = item && item.getContext && item.getContext('2d');

            // Load platform resources on first chart creation, to make possible to change
            // platform options after importing the library (e.g. `disableCSSInjection`).
            this._ensureLoaded();

            // `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
            // inside an iframe or when running in a protected environment. We could guess the
            // types from their toString() value but let's keep things flexible and assume it's
            // a sufficient condition if the item has a context2D which has item as `canvas`.
            // https://github.com/chartjs/Chart.js/issues/3887
            // https://github.com/chartjs/Chart.js/issues/4102
            // https://github.com/chartjs/Chart.js/issues/4152
            if (context && context.canvas === item) {
                initCanvas(item, config);
                return context;
            }

            return null;
        },

        releaseContext: function (context) {
            var canvas = context.canvas;
            if (!canvas[EXPANDO_KEY]) {
                return;
            }

            var initial = canvas[EXPANDO_KEY].initial;
            ['height', 'width'].forEach(function (prop) {
                var value = initial[prop];
                if (helpers$1.isNullOrUndef(value)) {
                    canvas.removeAttribute(prop);
                } else {
                    canvas.setAttribute(prop, value);
                }
            });

            helpers$1.each(initial.style || {}, function (value, key) {
                canvas.style[key] = value;
            });

            // The canvas render size might have been changed (and thus the state stack discarded),
            // we can't use save() and restore() to restore the initial state. So make sure that at
            // least the canvas context is reset to the default state by setting the canvas width.
            // https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
            // eslint-disable-next-line no-self-assign
            canvas.width = canvas.width;

            delete canvas[EXPANDO_KEY];
        },

        addEventListener: function (chart, type, listener) {
            var canvas = chart.canvas;
            if (type === 'resize') {
                // Note: the resize event is not supported on all browsers.
                addResizeListener(canvas, listener, chart);
                return;
            }

            var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
            var proxies = expando.proxies || (expando.proxies = {});
            var proxy = proxies[chart.id + '_' + type] = function (event) {
                listener(fromNativeEvent(event, chart));
            };

            addListener(canvas, type, proxy);
        },

        removeEventListener: function (chart, type, listener) {
            var canvas = chart.canvas;
            if (type === 'resize') {
                // Note: the resize event is not supported on all browsers.
                removeResizeListener(canvas);
                return;
            }

            var expando = listener[EXPANDO_KEY] || {};
            var proxies = expando.proxies || {};
            var proxy = proxies[chart.id + '_' + type];
            if (!proxy) {
                return;
            }

            removeListener(canvas, type, proxy);
        }
    };

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, use EventTarget.addEventListener instead.
     * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
     * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
     * @function Chart.helpers.addEvent
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers$1.addEvent = addListener;

    /**
     * Provided for backward compatibility, use EventTarget.removeEventListener instead.
     * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
     * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
     * @function Chart.helpers.removeEvent
     * @deprecated since version 2.7.0
     * @todo remove at version 3
     * @private
     */
    helpers$1.removeEvent = removeListener;

    // @TODO Make possible to select another platform at build time.
    var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic;

    /**
     * @namespace Chart.platform
     * @see https://chartjs.gitbooks.io/proposals/content/Platform.html
     * @since 2.4.0
     */
    var platform = helpers$1.extend({
        /**
         * @since 2.7.0
         */
        initialize: function () { },

        /**
         * Called at chart construction time, returns a context2d instance implementing
         * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
         * @param {*} item - The native item from which to acquire context (platform specific)
         * @param {object} options - The chart options
         * @returns {CanvasRenderingContext2D} context2d instance
         */
        acquireContext: function () { },

        /**
         * Called at chart destruction time, releases any resources associated to the context
         * previously returned by the acquireContext() method.
         * @param {CanvasRenderingContext2D} context - The context2d instance
         * @returns {boolean} true if the method succeeded, else false
         */
        releaseContext: function () { },

        /**
         * Registers the specified listener on the given chart.
         * @param {Chart} chart - Chart from which to listen for event
         * @param {string} type - The ({@link IEvent}) type to listen for
         * @param {function} listener - Receives a notification (an object that implements
         * the {@link IEvent} interface) when an event of the specified type occurs.
         */
        addEventListener: function () { },

        /**
         * Removes the specified listener previously registered with addEventListener.
         * @param {Chart} chart - Chart from which to remove the listener
         * @param {string} type - The ({@link IEvent}) type to remove
         * @param {function} listener - The listener function to remove from the event target.
         */
        removeEventListener: function () { }

    }, implementation);

    core_defaults._set('global', {
        plugins: {}
    });

    /**
     * The plugin service singleton
     * @namespace Chart.plugins
     * @since 2.1.0
     */
    var core_plugins = {
        /**
         * Globally registered plugins.
         * @private
         */
        _plugins: [],

        /**
         * This identifier is used to invalidate the descriptors cache attached to each chart
         * when a global plugin is registered or unregistered. In this case, the cache ID is
         * incremented and descriptors are regenerated during following API calls.
         * @private
         */
        _cacheId: 0,

        /**
         * Registers the given plugin(s) if not already registered.
         * @param {IPlugin[]|IPlugin} plugins plugin instance(s).
         */
        register: function (plugins) {
            var p = this._plugins;
            ([]).concat(plugins).forEach(function (plugin) {
                if (p.indexOf(plugin) === -1) {
                    p.push(plugin);
                }
            });

            this._cacheId++;
        },

        /**
         * Unregisters the given plugin(s) only if registered.
         * @param {IPlugin[]|IPlugin} plugins plugin instance(s).
         */
        unregister: function (plugins) {
            var p = this._plugins;
            ([]).concat(plugins).forEach(function (plugin) {
                var idx = p.indexOf(plugin);
                if (idx !== -1) {
                    p.splice(idx, 1);
                }
            });

            this._cacheId++;
        },

        /**
         * Remove all registered plugins.
         * @since 2.1.5
         */
        clear: function () {
            this._plugins = [];
            this._cacheId++;
        },

        /**
         * Returns the number of registered plugins?
         * @returns {number}
         * @since 2.1.5
         */
        count: function () {
            return this._plugins.length;
        },

        /**
         * Returns all registered plugin instances.
         * @returns {IPlugin[]} array of plugin objects.
         * @since 2.1.5
         */
        getAll: function () {
            return this._plugins;
        },

        /**
         * Calls enabled plugins for `chart` on the specified hook and with the given args.
         * This method immediately returns as soon as a plugin explicitly returns false. The
         * returned value can be used, for instance, to interrupt the current action.
         * @param {Chart} chart - The chart instance for which plugins should be called.
         * @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
         * @param {Array} [args] - Extra arguments to apply to the hook call.
         * @returns {boolean} false if any of the plugins return false, else returns true.
         */
        notify: function (chart, hook, args) {
            var descriptors = this.descriptors(chart);
            var ilen = descriptors.length;
            var i, descriptor, plugin, params, method;

            for (i = 0; i < ilen; ++i) {
                descriptor = descriptors[i];
                plugin = descriptor.plugin;
                method = plugin[hook];
                if (typeof method === 'function') {
                    params = [chart].concat(args || []);
                    params.push(descriptor.options);
                    if (method.apply(plugin, params) === false) {
                        return false;
                    }
                }
            }

            return true;
        },

        /**
         * Returns descriptors of enabled plugins for the given chart.
         * @returns {object[]} [{ plugin, options }]
         * @private
         */
        descriptors: function (chart) {
            var cache = chart.$plugins || (chart.$plugins = {});
            if (cache.id === this._cacheId) {
                return cache.descriptors;
            }

            var plugins = [];
            var descriptors = [];
            var config = (chart && chart.config) || {};
            var options = (config.options && config.options.plugins) || {};

            this._plugins.concat(config.plugins || []).forEach(function (plugin) {
                var idx = plugins.indexOf(plugin);
                if (idx !== -1) {
                    return;
                }

                var id = plugin.id;
                var opts = options[id];
                if (opts === false) {
                    return;
                }

                if (opts === true) {
                    opts = helpers$1.clone(core_defaults.global.plugins[id]);
                }

                plugins.push(plugin);
                descriptors.push({
                    plugin: plugin,
                    options: opts || {}
                });
            });

            cache.descriptors = descriptors;
            cache.id = this._cacheId;
            return descriptors;
        },

        /**
         * Invalidates cache for the given chart: descriptors hold a reference on plugin option,
         * but in some cases, this reference can be changed by the user when updating options.
         * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
         * @private
         */
        _invalidate: function (chart) {
            delete chart.$plugins;
        }
    };

    var core_scaleService = {
        // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
        // use the new chart options to grab the correct scale
        constructors: {},
        // Use a registration function so that we can move to an ES6 map when we no longer need to support
        // old browsers

        // Scale config defaults
        defaults: {},
        registerScaleType: function (type, scaleConstructor, scaleDefaults) {
            this.constructors[type] = scaleConstructor;
            this.defaults[type] = helpers$1.clone(scaleDefaults);
        },
        getScaleConstructor: function (type) {
            return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
        },
        getScaleDefaults: function (type) {
            // Return the scale defaults merged with the global settings so that we always use the latest ones
            return this.defaults.hasOwnProperty(type) ? helpers$1.merge({}, [core_defaults.scale, this.defaults[type]]) : {};
        },
        updateScaleDefaults: function (type, additions) {
            var me = this;
            if (me.defaults.hasOwnProperty(type)) {
                me.defaults[type] = helpers$1.extend(me.defaults[type], additions);
            }
        },
        addScalesToLayout: function (chart) {
            // Adds each scale to the chart.boxes array to be sized accordingly
            helpers$1.each(chart.scales, function (scale) {
                // Set ILayoutItem parameters for backwards compatibility
                scale.fullWidth = scale.options.fullWidth;
                scale.position = scale.options.position;
                scale.weight = scale.options.weight;
                core_layouts.addBox(chart, scale);
            });
        }
    };

    var valueOrDefault$7 = helpers$1.valueOrDefault;

    core_defaults._set('global', {
        tooltips: {
            enabled: true,
            custom: null,
            mode: 'nearest',
            position: 'average',
            intersect: true,
            backgroundColor: 'rgba(0,0,0,0.8)',
            titleFontStyle: 'bold',
            titleSpacing: 2,
            titleMarginBottom: 6,
            titleFontColor: '#fff',
            titleAlign: 'left',
            bodySpacing: 2,
            bodyFontColor: '#fff',
            bodyAlign: 'left',
            footerFontStyle: 'bold',
            footerSpacing: 2,
            footerMarginTop: 6,
            footerFontColor: '#fff',
            footerAlign: 'left',
            yPadding: 6,
            xPadding: 6,
            caretPadding: 2,
            caretSize: 5,
            cornerRadius: 6,
            multiKeyBackground: '#fff',
            displayColors: true,
            borderColor: 'rgba(0,0,0,0)',
            borderWidth: 0,
            callbacks: {
                // Args are: (tooltipItems, data)
                beforeTitle: helpers$1.noop,
                title: function (tooltipItems, data) {
                    var title = '';
                    var labels = data.labels;
                    var labelCount = labels ? labels.length : 0;

                    if (tooltipItems.length > 0) {
                        var item = tooltipItems[0];
                        if (item.label) {
                            title = item.label;
                        } else if (item.xLabel) {
                            title = item.xLabel;
                        } else if (labelCount > 0 && item.index < labelCount) {
                            title = labels[item.index];
                        }
                    }

                    return title;
                },
                afterTitle: helpers$1.noop,

                // Args are: (tooltipItems, data)
                beforeBody: helpers$1.noop,

                // Args are: (tooltipItem, data)
                beforeLabel: helpers$1.noop,
                label: function (tooltipItem, data) {
                    var label = data.datasets[tooltipItem.datasetIndex].label || '';

                    if (label) {
                        label += ': ';
                    }
                    if (!helpers$1.isNullOrUndef(tooltipItem.value)) {
                        label += tooltipItem.value;
                    } else {
                        label += tooltipItem.yLabel;
                    }
                    return label;
                },
                labelColor: function (tooltipItem, chart) {
                    var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
                    var activeElement = meta.data[tooltipItem.index];
                    var view = activeElement._view;
                    return {
                        borderColor: view.borderColor,
                        backgroundColor: view.backgroundColor
                    };
                },
                labelTextColor: function () {
                    return this._options.bodyFontColor;
                },
                afterLabel: helpers$1.noop,

                // Args are: (tooltipItems, data)
                afterBody: helpers$1.noop,

                // Args are: (tooltipItems, data)
                beforeFooter: helpers$1.noop,
                footer: helpers$1.noop,
                afterFooter: helpers$1.noop
            }
        }
    });

    var positioners = {
        /**
         * Average mode places the tooltip at the average position of the elements shown
         * @function Chart.Tooltip.positioners.average
         * @param elements {ChartElement[]} the elements being displayed in the tooltip
         * @returns {object} tooltip position
         */
        average: function (elements) {
            if (!elements.length) {
                return false;
            }

            var i, len;
            var x = 0;
            var y = 0;
            var count = 0;

            for (i = 0, len = elements.length; i < len; ++i) {
                var el = elements[i];
                if (el && el.hasValue()) {
                    var pos = el.tooltipPosition();
                    x += pos.x;
                    y += pos.y;
                    ++count;
                }
            }

            return {
                x: x / count,
                y: y / count
            };
        },

        /**
         * Gets the tooltip position nearest of the item nearest to the event position
         * @function Chart.Tooltip.positioners.nearest
         * @param elements {Chart.Element[]} the tooltip elements
         * @param eventPosition {object} the position of the event in canvas coordinates
         * @returns {object} the tooltip position
         */
        nearest: function (elements, eventPosition) {
            var x = eventPosition.x;
            var y = eventPosition.y;
            var minDistance = Number.POSITIVE_INFINITY;
            var i, len, nearestElement;

            for (i = 0, len = elements.length; i < len; ++i) {
                var el = elements[i];
                if (el && el.hasValue()) {
                    var center = el.getCenterPoint();
                    var d = helpers$1.distanceBetweenPoints(eventPosition, center);

                    if (d < minDistance) {
                        minDistance = d;
                        nearestElement = el;
                    }
                }
            }

            if (nearestElement) {
                var tp = nearestElement.tooltipPosition();
                x = tp.x;
                y = tp.y;
            }

            return {
                x: x,
                y: y
            };
        }
    };

    // Helper to push or concat based on if the 2nd parameter is an array or not
    function pushOrConcat(base, toPush) {
        if (toPush) {
            if (helpers$1.isArray(toPush)) {
                // base = base.concat(toPush);
                Array.prototype.push.apply(base, toPush);
            } else {
                base.push(toPush);
            }
        }

        return base;
    }

    /**
     * Returns array of strings split by newline
     * @param {string} value - The value to split by newline.
     * @returns {string[]} value if newline present - Returned from String split() method
     * @function
     */
    function splitNewlines(str) {
        if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
            return str.split('\n');
        }
        return str;
    }


    /**
     * Private helper to create a tooltip item model
     * @param element - the chart element (point, arc, bar) to create the tooltip item for
     * @return new tooltip item
     */
    function createTooltipItem(element) {
        var xScale = element._xScale;
        var yScale = element._yScale || element._scale; // handle radar || polarArea charts
        var index = element._index;
        var datasetIndex = element._datasetIndex;
        var controller = element._chart.getDatasetMeta(datasetIndex).controller;
        var indexScale = controller._getIndexScale();
        var valueScale = controller._getValueScale();

        return {
            xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
            yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
            label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '',
            value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '',
            index: index,
            datasetIndex: datasetIndex,
            x: element._model.x,
            y: element._model.y
        };
    }

    /**
     * Helper to get the reset model for the tooltip
     * @param tooltipOpts {object} the tooltip options
     */
    function getBaseModel(tooltipOpts) {
        var globalDefaults = core_defaults.global;

        return {
            // Positioning
            xPadding: tooltipOpts.xPadding,
            yPadding: tooltipOpts.yPadding,
            xAlign: tooltipOpts.xAlign,
            yAlign: tooltipOpts.yAlign,

            // Body
            bodyFontColor: tooltipOpts.bodyFontColor,
            _bodyFontFamily: valueOrDefault$7(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
            _bodyFontStyle: valueOrDefault$7(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
            _bodyAlign: tooltipOpts.bodyAlign,
            bodyFontSize: valueOrDefault$7(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
            bodySpacing: tooltipOpts.bodySpacing,

            // Title
            titleFontColor: tooltipOpts.titleFontColor,
            _titleFontFamily: valueOrDefault$7(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
            _titleFontStyle: valueOrDefault$7(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
            titleFontSize: valueOrDefault$7(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
            _titleAlign: tooltipOpts.titleAlign,
            titleSpacing: tooltipOpts.titleSpacing,
            titleMarginBottom: tooltipOpts.titleMarginBottom,

            // Footer
            footerFontColor: tooltipOpts.footerFontColor,
            _footerFontFamily: valueOrDefault$7(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
            _footerFontStyle: valueOrDefault$7(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
            footerFontSize: valueOrDefault$7(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
            _footerAlign: tooltipOpts.footerAlign,
            footerSpacing: tooltipOpts.footerSpacing,
            footerMarginTop: tooltipOpts.footerMarginTop,

            // Appearance
            caretSize: tooltipOpts.caretSize,
            cornerRadius: tooltipOpts.cornerRadius,
            backgroundColor: tooltipOpts.backgroundColor,
            opacity: 0,
            legendColorBackground: tooltipOpts.multiKeyBackground,
            displayColors: tooltipOpts.displayColors,
            borderColor: tooltipOpts.borderColor,
            borderWidth: tooltipOpts.borderWidth
        };
    }

    /**
     * Get the size of the tooltip
     */
    function getTooltipSize(tooltip, model) {
        var ctx = tooltip._chart.ctx;

        var height = model.yPadding * 2; // Tooltip Padding
        var width = 0;

        // Count of all lines in the body
        var body = model.body;
        var combinedBodyLength = body.reduce(function (count, bodyItem) {
            return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
        }, 0);
        combinedBodyLength += model.beforeBody.length + model.afterBody.length;

        var titleLineCount = model.title.length;
        var footerLineCount = model.footer.length;
        var titleFontSize = model.titleFontSize;
        var bodyFontSize = model.bodyFontSize;
        var footerFontSize = model.footerFontSize;

        height += titleLineCount * titleFontSize; // Title Lines
        height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
        height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
        height += combinedBodyLength * bodyFontSize; // Body Lines
        height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
        height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
        height += footerLineCount * (footerFontSize); // Footer Lines
        height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing

        // Title width
        var widthPadding = 0;
        var maxLineWidth = function (line) {
            width = Math.max(width, ctx.measureText(line).width + widthPadding);
        };

        ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
        helpers$1.each(model.title, maxLineWidth);

        // Body width
        ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
        helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth);

        // Body lines may include some extra width due to the color box
        widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
        helpers$1.each(body, function (bodyItem) {
            helpers$1.each(bodyItem.before, maxLineWidth);
            helpers$1.each(bodyItem.lines, maxLineWidth);
            helpers$1.each(bodyItem.after, maxLineWidth);
        });

        // Reset back to 0
        widthPadding = 0;

        // Footer width
        ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
        helpers$1.each(model.footer, maxLineWidth);

        // Add padding
        width += 2 * model.xPadding;

        return {
            width: width,
            height: height
        };
    }

    /**
     * Helper to get the alignment of a tooltip given the size
     */
    function determineAlignment(tooltip, size) {
        var model = tooltip._model;
        var chart = tooltip._chart;
        var chartArea = tooltip._chart.chartArea;
        var xAlign = 'center';
        var yAlign = 'center';

        if (model.y < size.height) {
            yAlign = 'top';
        } else if (model.y > (chart.height - size.height)) {
            yAlign = 'bottom';
        }

        var lf, rf; // functions to determine left, right alignment
        var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
        var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
        var midX = (chartArea.left + chartArea.right) / 2;
        var midY = (chartArea.top + chartArea.bottom) / 2;

        if (yAlign === 'center') {
            lf = function (x) {
                return x <= midX;
            };
            rf = function (x) {
                return x > midX;
            };
        } else {
            lf = function (x) {
                return x <= (size.width / 2);
            };
            rf = function (x) {
                return x >= (chart.width - (size.width / 2));
            };
        }

        olf = function (x) {
            return x + size.width + model.caretSize + model.caretPadding > chart.width;
        };
        orf = function (x) {
            return x - size.width - model.caretSize - model.caretPadding < 0;
        };
        yf = function (y) {
            return y <= midY ? 'top' : 'bottom';
        };

        if (lf(model.x)) {
            xAlign = 'left';

            // Is tooltip too wide and goes over the right side of the chart.?
            if (olf(model.x)) {
                xAlign = 'center';
                yAlign = yf(model.y);
            }
        } else if (rf(model.x)) {
            xAlign = 'right';

            // Is tooltip too wide and goes outside left edge of canvas?
            if (orf(model.x)) {
                xAlign = 'center';
                yAlign = yf(model.y);
            }
        }

        var opts = tooltip._options;
        return {
            xAlign: opts.xAlign ? opts.xAlign : xAlign,
            yAlign: opts.yAlign ? opts.yAlign : yAlign
        };
    }

    /**
     * Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
     */
    function getBackgroundPoint(vm, size, alignment, chart) {
        // Background Position
        var x = vm.x;
        var y = vm.y;

        var caretSize = vm.caretSize;
        var caretPadding = vm.caretPadding;
        var cornerRadius = vm.cornerRadius;
        var xAlign = alignment.xAlign;
        var yAlign = alignment.yAlign;
        var paddingAndSize = caretSize + caretPadding;
        var radiusAndPadding = cornerRadius + caretPadding;

        if (xAlign === 'right') {
            x -= size.width;
        } else if (xAlign === 'center') {
            x -= (size.width / 2);
            if (x + size.width > chart.width) {
                x = chart.width - size.width;
            }
            if (x < 0) {
                x = 0;
            }
        }

        if (yAlign === 'top') {
            y += paddingAndSize;
        } else if (yAlign === 'bottom') {
            y -= size.height + paddingAndSize;
        } else {
            y -= (size.height / 2);
        }

        if (yAlign === 'center') {
            if (xAlign === 'left') {
                x += paddingAndSize;
            } else if (xAlign === 'right') {
                x -= paddingAndSize;
            }
        } else if (xAlign === 'left') {
            x -= radiusAndPadding;
        } else if (xAlign === 'right') {
            x += radiusAndPadding;
        }

        return {
            x: x,
            y: y
        };
    }

    function getAlignedX(vm, align) {
        return align === 'center'
            ? vm.x + vm.width / 2
            : align === 'right'
                ? vm.x + vm.width - vm.xPadding
                : vm.x + vm.xPadding;
    }

    /**
     * Helper to build before and after body lines
     */
    function getBeforeAfterBodyLines(callback) {
        return pushOrConcat([], splitNewlines(callback));
    }

    var exports$3 = core_element.extend({
        initialize: function () {
            this._model = getBaseModel(this._options);
            this._lastActive = [];
        },

        // Get the title
        // Args are: (tooltipItem, data)
        getTitle: function () {
            var me = this;
            var opts = me._options;
            var callbacks = opts.callbacks;

            var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
            var title = callbacks.title.apply(me, arguments);
            var afterTitle = callbacks.afterTitle.apply(me, arguments);

            var lines = [];
            lines = pushOrConcat(lines, splitNewlines(beforeTitle));
            lines = pushOrConcat(lines, splitNewlines(title));
            lines = pushOrConcat(lines, splitNewlines(afterTitle));

            return lines;
        },

        // Args are: (tooltipItem, data)
        getBeforeBody: function () {
            return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
        },

        // Args are: (tooltipItem, data)
        getBody: function (tooltipItems, data) {
            var me = this;
            var callbacks = me._options.callbacks;
            var bodyItems = [];

            helpers$1.each(tooltipItems, function (tooltipItem) {
                var bodyItem = {
                    before: [],
                    lines: [],
                    after: []
                };
                pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
                pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
                pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));

                bodyItems.push(bodyItem);
            });

            return bodyItems;
        },

        // Args are: (tooltipItem, data)
        getAfterBody: function () {
            return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
        },

        // Get the footer and beforeFooter and afterFooter lines
        // Args are: (tooltipItem, data)
        getFooter: function () {
            var me = this;
            var callbacks = me._options.callbacks;

            var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
            var footer = callbacks.footer.apply(me, arguments);
            var afterFooter = callbacks.afterFooter.apply(me, arguments);

            var lines = [];
            lines = pushOrConcat(lines, splitNewlines(beforeFooter));
            lines = pushOrConcat(lines, splitNewlines(footer));
            lines = pushOrConcat(lines, splitNewlines(afterFooter));

            return lines;
        },

        update: function (changed) {
            var me = this;
            var opts = me._options;

            // Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
            // that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
            // which breaks any animations.
            var existingModel = me._model;
            var model = me._model = getBaseModel(opts);
            var active = me._active;

            var data = me._data;

            // In the case where active.length === 0 we need to keep these at existing values for good animations
            var alignment = {
                xAlign: existingModel.xAlign,
                yAlign: existingModel.yAlign
            };
            var backgroundPoint = {
                x: existingModel.x,
                y: existingModel.y
            };
            var tooltipSize = {
                width: existingModel.width,
                height: existingModel.height
            };
            var tooltipPosition = {
                x: existingModel.caretX,
                y: existingModel.caretY
            };

            var i, len;

            if (active.length) {
                model.opacity = 1;

                var labelColors = [];
                var labelTextColors = [];
                tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);

                var tooltipItems = [];
                for (i = 0, len = active.length; i < len; ++i) {
                    tooltipItems.push(createTooltipItem(active[i]));
                }

                // If the user provided a filter function, use it to modify the tooltip items
                if (opts.filter) {
                    tooltipItems = tooltipItems.filter(function (a) {
                        return opts.filter(a, data);
                    });
                }

                // If the user provided a sorting function, use it to modify the tooltip items
                if (opts.itemSort) {
                    tooltipItems = tooltipItems.sort(function (a, b) {
                        return opts.itemSort(a, b, data);
                    });
                }

                // Determine colors for boxes
                helpers$1.each(tooltipItems, function (tooltipItem) {
                    labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
                    labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
                });


                // Build the Text Lines
                model.title = me.getTitle(tooltipItems, data);
                model.beforeBody = me.getBeforeBody(tooltipItems, data);
                model.body = me.getBody(tooltipItems, data);
                model.afterBody = me.getAfterBody(tooltipItems, data);
                model.footer = me.getFooter(tooltipItems, data);

                // Initial positioning and colors
                model.x = tooltipPosition.x;
                model.y = tooltipPosition.y;
                model.caretPadding = opts.caretPadding;
                model.labelColors = labelColors;
                model.labelTextColors = labelTextColors;

                // data points
                model.dataPoints = tooltipItems;

                // We need to determine alignment of the tooltip
                tooltipSize = getTooltipSize(this, model);
                alignment = determineAlignment(this, tooltipSize);
                // Final Size and Position
                backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
            } else {
                model.opacity = 0;
            }

            model.xAlign = alignment.xAlign;
            model.yAlign = alignment.yAlign;
            model.x = backgroundPoint.x;
            model.y = backgroundPoint.y;
            model.width = tooltipSize.width;
            model.height = tooltipSize.height;

            // Point where the caret on the tooltip points to
            model.caretX = tooltipPosition.x;
            model.caretY = tooltipPosition.y;

            me._model = model;

            if (changed && opts.custom) {
                opts.custom.call(me, model);
            }

            return me;
        },

        drawCaret: function (tooltipPoint, size) {
            var ctx = this._chart.ctx;
            var vm = this._view;
            var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);

            ctx.lineTo(caretPosition.x1, caretPosition.y1);
            ctx.lineTo(caretPosition.x2, caretPosition.y2);
            ctx.lineTo(caretPosition.x3, caretPosition.y3);
        },
        getCaretPosition: function (tooltipPoint, size, vm) {
            var x1, x2, x3, y1, y2, y3;
            var caretSize = vm.caretSize;
            var cornerRadius = vm.cornerRadius;
            var xAlign = vm.xAlign;
            var yAlign = vm.yAlign;
            var ptX = tooltipPoint.x;
            var ptY = tooltipPoint.y;
            var width = size.width;
            var height = size.height;

            if (yAlign === 'center') {
                y2 = ptY + (height / 2);

                if (xAlign === 'left') {
                    x1 = ptX;
                    x2 = x1 - caretSize;
                    x3 = x1;

                    y1 = y2 + caretSize;
                    y3 = y2 - caretSize;
                } else {
                    x1 = ptX + width;
                    x2 = x1 + caretSize;
                    x3 = x1;

                    y1 = y2 - caretSize;
                    y3 = y2 + caretSize;
                }
            } else {
                if (xAlign === 'left') {
                    x2 = ptX + cornerRadius + (caretSize);
                    x1 = x2 - caretSize;
                    x3 = x2 + caretSize;
                } else if (xAlign === 'right') {
                    x2 = ptX + width - cornerRadius - caretSize;
                    x1 = x2 - caretSize;
                    x3 = x2 + caretSize;
                } else {
                    x2 = vm.caretX;
                    x1 = x2 - caretSize;
                    x3 = x2 + caretSize;
                }
                if (yAlign === 'top') {
                    y1 = ptY;
                    y2 = y1 - caretSize;
                    y3 = y1;
                } else {
                    y1 = ptY + height;
                    y2 = y1 + caretSize;
                    y3 = y1;
                    // invert drawing order
                    var tmp = x3;
                    x3 = x1;
                    x1 = tmp;
                }
            }
            return { x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3 };
        },

        drawTitle: function (pt, vm, ctx) {
            var title = vm.title;

            if (title.length) {
                pt.x = getAlignedX(vm, vm._titleAlign);

                ctx.textAlign = vm._titleAlign;
                ctx.textBaseline = 'top';

                var titleFontSize = vm.titleFontSize;
                var titleSpacing = vm.titleSpacing;

                ctx.fillStyle = vm.titleFontColor;
                ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);

                var i, len;
                for (i = 0, len = title.length; i < len; ++i) {
                    ctx.fillText(title[i], pt.x, pt.y);
                    pt.y += titleFontSize + titleSpacing; // Line Height and spacing

                    if (i + 1 === title.length) {
                        pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
                    }
                }
            }
        },

        drawBody: function (pt, vm, ctx) {
            var bodyFontSize = vm.bodyFontSize;
            var bodySpacing = vm.bodySpacing;
            var bodyAlign = vm._bodyAlign;
            var body = vm.body;
            var drawColorBoxes = vm.displayColors;
            var labelColors = vm.labelColors;
            var xLinePadding = 0;
            var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0;
            var textColor;

            ctx.textAlign = bodyAlign;
            ctx.textBaseline = 'top';
            ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);

            pt.x = getAlignedX(vm, bodyAlign);

            // Before Body
            var fillLineOfText = function (line) {
                ctx.fillText(line, pt.x + xLinePadding, pt.y);
                pt.y += bodyFontSize + bodySpacing;
            };

            // Before body lines
            ctx.fillStyle = vm.bodyFontColor;
            helpers$1.each(vm.beforeBody, fillLineOfText);

            xLinePadding = drawColorBoxes && bodyAlign !== 'right'
                ? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
                : 0;

            // Draw body lines now
            helpers$1.each(body, function (bodyItem, i) {
                textColor = vm.labelTextColors[i];
                ctx.fillStyle = textColor;
                helpers$1.each(bodyItem.before, fillLineOfText);

                helpers$1.each(bodyItem.lines, function (line) {
                    // Draw Legend-like boxes if needed
                    if (drawColorBoxes) {
                        // Fill a white rect so that colours merge nicely if the opacity is < 1
                        ctx.fillStyle = vm.legendColorBackground;
                        ctx.fillRect(colorX, pt.y, bodyFontSize, bodyFontSize);

                        // Border
                        ctx.lineWidth = 1;
                        ctx.strokeStyle = labelColors[i].borderColor;
                        ctx.strokeRect(colorX, pt.y, bodyFontSize, bodyFontSize);

                        // Inner square
                        ctx.fillStyle = labelColors[i].backgroundColor;
                        ctx.fillRect(colorX + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
                        ctx.fillStyle = textColor;
                    }

                    fillLineOfText(line);
                });

                helpers$1.each(bodyItem.after, fillLineOfText);
            });

            // Reset back to 0 for after body
            xLinePadding = 0;

            // After body lines
            helpers$1.each(vm.afterBody, fillLineOfText);
            pt.y -= bodySpacing; // Remove last body spacing
        },

        drawFooter: function (pt, vm, ctx) {
            var footer = vm.footer;

            if (footer.length) {
                pt.x = getAlignedX(vm, vm._footerAlign);
                pt.y += vm.footerMarginTop;

                ctx.textAlign = vm._footerAlign;
                ctx.textBaseline = 'top';

                ctx.fillStyle = vm.footerFontColor;
                ctx.font = helpers$1.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);

                helpers$1.each(footer, function (line) {
                    ctx.fillText(line, pt.x, pt.y);
                    pt.y += vm.footerFontSize + vm.footerSpacing;
                });
            }
        },

        drawBackground: function (pt, vm, ctx, tooltipSize) {
            ctx.fillStyle = vm.backgroundColor;
            ctx.strokeStyle = vm.borderColor;
            ctx.lineWidth = vm.borderWidth;
            var xAlign = vm.xAlign;
            var yAlign = vm.yAlign;
            var x = pt.x;
            var y = pt.y;
            var width = tooltipSize.width;
            var height = tooltipSize.height;
            var radius = vm.cornerRadius;

            ctx.beginPath();
            ctx.moveTo(x + radius, y);
            if (yAlign === 'top') {
                this.drawCaret(pt, tooltipSize);
            }
            ctx.lineTo(x + width - radius, y);
            ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
            if (yAlign === 'center' && xAlign === 'right') {
                this.drawCaret(pt, tooltipSize);
            }
            ctx.lineTo(x + width, y + height - radius);
            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
            if (yAlign === 'bottom') {
                this.drawCaret(pt, tooltipSize);
            }
            ctx.lineTo(x + radius, y + height);
            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
            if (yAlign === 'center' && xAlign === 'left') {
                this.drawCaret(pt, tooltipSize);
            }
            ctx.lineTo(x, y + radius);
            ctx.quadraticCurveTo(x, y, x + radius, y);
            ctx.closePath();

            ctx.fill();

            if (vm.borderWidth > 0) {
                ctx.stroke();
            }
        },

        draw: function () {
            var ctx = this._chart.ctx;
            var vm = this._view;

            if (vm.opacity === 0) {
                return;
            }

            var tooltipSize = {
                width: vm.width,
                height: vm.height
            };
            var pt = {
                x: vm.x,
                y: vm.y
            };

            // IE11/Edge does not like very small opacities, so snap to 0
            var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;

            // Truthy/falsey value for empty tooltip
            var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;

            if (this._options.enabled && hasTooltipContent) {
                ctx.save();
                ctx.globalAlpha = opacity;

                // Draw Background
                this.drawBackground(pt, vm, ctx, tooltipSize);

                // Draw Title, Body, and Footer
                pt.y += vm.yPadding;

                // Titles
                this.drawTitle(pt, vm, ctx);

                // Body
                this.drawBody(pt, vm, ctx);

                // Footer
                this.drawFooter(pt, vm, ctx);

                ctx.restore();
            }
        },

        /**
         * Handle an event
         * @private
         * @param {IEvent} event - The event to handle
         * @returns {boolean} true if the tooltip changed
         */
        handleEvent: function (e) {
            var me = this;
            var options = me._options;
            var changed = false;

            me._lastActive = me._lastActive || [];

            // Find Active Elements for tooltips
            if (e.type === 'mouseout') {
                me._active = [];
            } else {
                me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
            }

            // Remember Last Actives
            changed = !helpers$1.arrayEquals(me._active, me._lastActive);

            // Only handle target event on tooltip change
            if (changed) {
                me._lastActive = me._active;

                if (options.enabled || options.custom) {
                    me._eventPosition = {
                        x: e.x,
                        y: e.y
                    };

                    me.update(true);
                    me.pivot();
                }
            }

            return changed;
        }
    });

    /**
     * @namespace Chart.Tooltip.positioners
     */
    var positioners_1 = positioners;

    var core_tooltip = exports$3;
    core_tooltip.positioners = positioners_1;

    var valueOrDefault$8 = helpers$1.valueOrDefault;

    core_defaults._set('global', {
        elements: {},
        events: [
            'mousemove',
            'mouseout',
            'click',
            'touchstart',
            'touchmove'
        ],
        hover: {
            onHover: null,
            mode: 'nearest',
            intersect: true,
            animationDuration: 400
        },
        onClick: null,
        maintainAspectRatio: true,
        responsive: true,
        responsiveAnimationDuration: 0
    });

    /**
     * Recursively merge the given config objects representing the `scales` option
     * by incorporating scale defaults in `xAxes` and `yAxes` array items, then
     * returns a deep copy of the result, thus doesn't alter inputs.
     */
    function mergeScaleConfig(/* config objects ... */) {
        return helpers$1.merge({}, [].slice.call(arguments), {
            merger: function (key, target, source, options) {
                if (key === 'xAxes' || key === 'yAxes') {
                    var slen = source[key].length;
                    var i, type, scale;

                    if (!target[key]) {
                        target[key] = [];
                    }

                    for (i = 0; i < slen; ++i) {
                        scale = source[key][i];
                        type = valueOrDefault$8(scale.type, key === 'xAxes' ? 'category' : 'linear');

                        if (i >= target[key].length) {
                            target[key].push({});
                        }

                        if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
                            // new/untyped scale or type changed: let's apply the new defaults
                            // then merge source scale to correctly overwrite the defaults.
                            helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]);
                        } else {
                            // scales type are the same
                            helpers$1.merge(target[key][i], scale);
                        }
                    }
                } else {
                    helpers$1._merger(key, target, source, options);
                }
            }
        });
    }

    /**
     * Recursively merge the given config objects as the root options by handling
     * default scale options for the `scales` and `scale` properties, then returns
     * a deep copy of the result, thus doesn't alter inputs.
     */
    function mergeConfig(/* config objects ... */) {
        return helpers$1.merge({}, [].slice.call(arguments), {
            merger: function (key, target, source, options) {
                var tval = target[key] || {};
                var sval = source[key];

                if (key === 'scales') {
                    // scale config merging is complex. Add our own function here for that
                    target[key] = mergeScaleConfig(tval, sval);
                } else if (key === 'scale') {
                    // used in polar area & radar charts since there is only one scale
                    target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]);
                } else {
                    helpers$1._merger(key, target, source, options);
                }
            }
        });
    }

    function initConfig(config) {
        config = config || {};

        // Do NOT use mergeConfig for the data object because this method merges arrays
        // and so would change references to labels and datasets, preventing data updates.
        var data = config.data = config.data || {};
        data.datasets = data.datasets || [];
        data.labels = data.labels || [];

        config.options = mergeConfig(
            core_defaults.global,
            core_defaults[config.type],
            config.options || {});

        return config;
    }

    function updateConfig(chart) {
        var newOptions = chart.options;

        helpers$1.each(chart.scales, function (scale) {
            core_layouts.removeBox(chart, scale);
        });

        newOptions = mergeConfig(
            core_defaults.global,
            core_defaults[chart.config.type],
            newOptions);

        chart.options = chart.config.options = newOptions;
        chart.ensureScalesHaveIDs();
        chart.buildOrUpdateScales();

        // Tooltip
        chart.tooltip._options = newOptions.tooltips;
        chart.tooltip.initialize();
    }

    function positionIsHorizontal(position) {
        return position === 'top' || position === 'bottom';
    }

    var Chart = function (item, config) {
        this.construct(item, config);
        return this;
    };

    helpers$1.extend(Chart.prototype, /** @lends Chart */ {
        /**
         * @private
         */
        construct: function (item, config) {
            var me = this;

            config = initConfig(config);

            var context = platform.acquireContext(item, config);
            var canvas = context && context.canvas;
            var height = canvas && canvas.height;
            var width = canvas && canvas.width;

            me.id = helpers$1.uid();
            me.ctx = context;
            me.canvas = canvas;
            me.config = config;
            me.width = width;
            me.height = height;
            me.aspectRatio = height ? width / height : null;
            me.options = config.options;
            me._bufferedRender = false;

            /**
             * Provided for backward compatibility, Chart and Chart.Controller have been merged,
             * the "instance" still need to be defined since it might be called from plugins.
             * @prop Chart#chart
             * @deprecated since version 2.6.0
             * @todo remove at version 3
             * @private
             */
            me.chart = me;
            me.controller = me; // chart.chart.controller #inception

            // Add the chart instance to the global namespace
            Chart.instances[me.id] = me;

            // Define alias to the config data: `chart.data === chart.config.data`
            Object.defineProperty(me, 'data', {
                get: function () {
                    return me.config.data;
                },
                set: function (value) {
                    me.config.data = value;
                }
            });

            if (!context || !canvas) {
                // The given item is not a compatible context2d element, let's return before finalizing
                // the chart initialization but after setting basic chart / controller properties that
                // can help to figure out that the chart is not valid (e.g chart.canvas !== null);
                // https://github.com/chartjs/Chart.js/issues/2807
                console.error("Failed to create chart: can't acquire context from the given item");
                return;
            }

            me.initialize();
            me.update();
        },

        /**
         * @private
         */
        initialize: function () {
            var me = this;

            // Before init plugin notification
            core_plugins.notify(me, 'beforeInit');

            helpers$1.retinaScale(me, me.options.devicePixelRatio);

            me.bindEvents();

            if (me.options.responsive) {
                // Initial resize before chart draws (must be silent to preserve initial animations).
                me.resize(true);
            }

            // Make sure scales have IDs and are built before we build any controllers.
            me.ensureScalesHaveIDs();
            me.buildOrUpdateScales();
            me.initToolTip();

            // After init plugin notification
            core_plugins.notify(me, 'afterInit');

            return me;
        },

        clear: function () {
            helpers$1.canvas.clear(this);
            return this;
        },

        stop: function () {
            // Stops any current animation loop occurring
            core_animations.cancelAnimation(this);
            return this;
        },

        resize: function (silent) {
            var me = this;
            var options = me.options;
            var canvas = me.canvas;
            var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;

            // the canvas render width and height will be casted to integers so make sure that
            // the canvas display style uses the same integer values to avoid blurring effect.

            // Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
            var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas)));
            var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas)));

            if (me.width === newWidth && me.height === newHeight) {
                return;
            }

            canvas.width = me.width = newWidth;
            canvas.height = me.height = newHeight;
            canvas.style.width = newWidth + 'px';
            canvas.style.height = newHeight + 'px';

            helpers$1.retinaScale(me, options.devicePixelRatio);

            if (!silent) {
                // Notify any plugins about the resize
                var newSize = { width: newWidth, height: newHeight };
                core_plugins.notify(me, 'resize', [newSize]);

                // Notify of resize
                if (options.onResize) {
                    options.onResize(me, newSize);
                }

                me.stop();
                me.update({
                    duration: options.responsiveAnimationDuration
                });
            }
        },

        ensureScalesHaveIDs: function () {
            var options = this.options;
            var scalesOptions = options.scales || {};
            var scaleOptions = options.scale;

            helpers$1.each(scalesOptions.xAxes, function (xAxisOptions, index) {
                xAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);
            });

            helpers$1.each(scalesOptions.yAxes, function (yAxisOptions, index) {
                yAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);
            });

            if (scaleOptions) {
                scaleOptions.id = scaleOptions.id || 'scale';
            }
        },

        /**
         * Builds a map of scale ID to scale object for future lookup.
         */
        buildOrUpdateScales: function () {
            var me = this;
            var options = me.options;
            var scales = me.scales || {};
            var items = [];
            var updated = Object.keys(scales).reduce(function (obj, id) {
                obj[id] = false;
                return obj;
            }, {});

            if (options.scales) {
                items = items.concat(
                    (options.scales.xAxes || []).map(function (xAxisOptions) {
                        return { options: xAxisOptions, dtype: 'category', dposition: 'bottom' };
                    }),
                    (options.scales.yAxes || []).map(function (yAxisOptions) {
                        return { options: yAxisOptions, dtype: 'linear', dposition: 'left' };
                    })
                );
            }

            if (options.scale) {
                items.push({
                    options: options.scale,
                    dtype: 'radialLinear',
                    isDefault: true,
                    dposition: 'chartArea'
                });
            }

            helpers$1.each(items, function (item) {
                var scaleOptions = item.options;
                var id = scaleOptions.id;
                var scaleType = valueOrDefault$8(scaleOptions.type, item.dtype);

                if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
                    scaleOptions.position = item.dposition;
                }

                updated[id] = true;
                var scale = null;
                if (id in scales && scales[id].type === scaleType) {
                    scale = scales[id];
                    scale.options = scaleOptions;
                    scale.ctx = me.ctx;
                    scale.chart = me;
                } else {
                    var scaleClass = core_scaleService.getScaleConstructor(scaleType);
                    if (!scaleClass) {
                        return;
                    }
                    scale = new scaleClass({
                        id: id,
                        type: scaleType,
                        options: scaleOptions,
                        ctx: me.ctx,
                        chart: me
                    });
                    scales[scale.id] = scale;
                }

                scale.mergeTicksOptions();

                // TODO(SB): I think we should be able to remove this custom case (options.scale)
                // and consider it as a regular scale part of the "scales"" map only! This would
                // make the logic easier and remove some useless? custom code.
                if (item.isDefault) {
                    me.scale = scale;
                }
            });
            // clear up discarded scales
            helpers$1.each(updated, function (hasUpdated, id) {
                if (!hasUpdated) {
                    delete scales[id];
                }
            });

            me.scales = scales;

            core_scaleService.addScalesToLayout(this);
        },

        buildOrUpdateControllers: function () {
            var me = this;
            var newControllers = [];

            helpers$1.each(me.data.datasets, function (dataset, datasetIndex) {
                var meta = me.getDatasetMeta(datasetIndex);
                var type = dataset.type || me.config.type;

                if (meta.type && meta.type !== type) {
                    me.destroyDatasetMeta(datasetIndex);
                    meta = me.getDatasetMeta(datasetIndex);
                }
                meta.type = type;

                if (meta.controller) {
                    meta.controller.updateIndex(datasetIndex);
                    meta.controller.linkScales();
                } else {
                    var ControllerClass = controllers[meta.type];
                    if (ControllerClass === undefined) {
                        throw new Error('"' + meta.type + '" is not a chart type.');
                    }

                    meta.controller = new ControllerClass(me, datasetIndex);
                    newControllers.push(meta.controller);
                }
            }, me);

            return newControllers;
        },

        /**
         * Reset the elements of all datasets
         * @private
         */
        resetElements: function () {
            var me = this;
            helpers$1.each(me.data.datasets, function (dataset, datasetIndex) {
                me.getDatasetMeta(datasetIndex).controller.reset();
            }, me);
        },

        /**
        * Resets the chart back to it's state before the initial animation
        */
        reset: function () {
            this.resetElements();
            this.tooltip.initialize();
        },

        update: function (config) {
            var me = this;

            if (!config || typeof config !== 'object') {
                // backwards compatibility
                config = {
                    duration: config,
                    lazy: arguments[1]
                };
            }

            updateConfig(me);

            // plugins options references might have change, let's invalidate the cache
            // https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
            core_plugins._invalidate(me);

            if (core_plugins.notify(me, 'beforeUpdate') === false) {
                return;
            }

            // In case the entire data object changed
            me.tooltip._data = me.data;

            // Make sure dataset controllers are updated and new controllers are reset
            var newControllers = me.buildOrUpdateControllers();

            // Make sure all dataset controllers have correct meta data counts
            helpers$1.each(me.data.datasets, function (dataset, datasetIndex) {
                me.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();
            }, me);

            me.updateLayout();

            // Can only reset the new controllers after the scales have been updated
            if (me.options.animation && me.options.animation.duration) {
                helpers$1.each(newControllers, function (controller) {
                    controller.reset();
                });
            }

            me.updateDatasets();

            // Need to reset tooltip in case it is displayed with elements that are removed
            // after update.
            me.tooltip.initialize();

            // Last active contains items that were previously in the tooltip.
            // When we reset the tooltip, we need to clear it
            me.lastActive = [];

            // Do this before render so that any plugins that need final scale updates can use it
            core_plugins.notify(me, 'afterUpdate');

            if (me._bufferedRender) {
                me._bufferedRequest = {
                    duration: config.duration,
                    easing: config.easing,
                    lazy: config.lazy
                };
            } else {
                me.render(config);
            }
        },

        /**
         * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
         * hook, in which case, plugins will not be called on `afterLayout`.
         * @private
         */
        updateLayout: function () {
            var me = this;

            if (core_plugins.notify(me, 'beforeLayout') === false) {
                return;
            }

            core_layouts.update(this, this.width, this.height);

            /**
             * Provided for backward compatibility, use `afterLayout` instead.
             * @method IPlugin#afterScaleUpdate
             * @deprecated since version 2.5.0
             * @todo remove at version 3
             * @private
             */
            core_plugins.notify(me, 'afterScaleUpdate');
            core_plugins.notify(me, 'afterLayout');
        },

        /**
         * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
         * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
         * @private
         */
        updateDatasets: function () {
            var me = this;

            if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) {
                return;
            }

            for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
                me.updateDataset(i);
            }

            core_plugins.notify(me, 'afterDatasetsUpdate');
        },

        /**
         * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
         * hook, in which case, plugins will not be called on `afterDatasetUpdate`.
         * @private
         */
        updateDataset: function (index) {
            var me = this;
            var meta = me.getDatasetMeta(index);
            var args = {
                meta: meta,
                index: index
            };

            if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
                return;
            }

            meta.controller.update();

            core_plugins.notify(me, 'afterDatasetUpdate', [args]);
        },

        render: function (config) {
            var me = this;

            if (!config || typeof config !== 'object') {
                // backwards compatibility
                config = {
                    duration: config,
                    lazy: arguments[1]
                };
            }

            var animationOptions = me.options.animation;
            var duration = valueOrDefault$8(config.duration, animationOptions && animationOptions.duration);
            var lazy = config.lazy;

            if (core_plugins.notify(me, 'beforeRender') === false) {
                return;
            }

            var onComplete = function (animation) {
                core_plugins.notify(me, 'afterRender');
                helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me);
            };

            if (animationOptions && duration) {
                var animation = new core_animation({
                    numSteps: duration / 16.66, // 60 fps
                    easing: config.easing || animationOptions.easing,

                    render: function (chart, animationObject) {
                        var easingFunction = helpers$1.easing.effects[animationObject.easing];
                        var currentStep = animationObject.currentStep;
                        var stepDecimal = currentStep / animationObject.numSteps;

                        chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
                    },

                    onAnimationProgress: animationOptions.onProgress,
                    onAnimationComplete: onComplete
                });

                core_animations.addAnimation(me, animation, duration, lazy);
            } else {
                me.draw();

                // See https://github.com/chartjs/Chart.js/issues/3781
                onComplete(new core_animation({ numSteps: 0, chart: me }));
            }

            return me;
        },

        draw: function (easingValue) {
            var me = this;

            me.clear();

            if (helpers$1.isNullOrUndef(easingValue)) {
                easingValue = 1;
            }

            me.transition(easingValue);

            if (me.width <= 0 || me.height <= 0) {
                return;
            }

            if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
                return;
            }

            // Draw all the scales
            helpers$1.each(me.boxes, function (box) {
                box.draw(me.chartArea);
            }, me);

            me.drawDatasets(easingValue);
            me._drawTooltip(easingValue);

            core_plugins.notify(me, 'afterDraw', [easingValue]);
        },

        /**
         * @private
         */
        transition: function (easingValue) {
            var me = this;

            for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
                if (me.isDatasetVisible(i)) {
                    me.getDatasetMeta(i).controller.transition(easingValue);
                }
            }

            me.tooltip.transition(easingValue);
        },

        /**
         * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
         * hook, in which case, plugins will not be called on `afterDatasetsDraw`.
         * @private
         */
        drawDatasets: function (easingValue) {
            var me = this;

            if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
                return;
            }

            // Draw datasets reversed to support proper line stacking
            for (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {
                if (me.isDatasetVisible(i)) {
                    me.drawDataset(i, easingValue);
                }
            }

            core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
        },

        /**
         * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
         * hook, in which case, plugins will not be called on `afterDatasetDraw`.
         * @private
         */
        drawDataset: function (index, easingValue) {
            var me = this;
            var meta = me.getDatasetMeta(index);
            var args = {
                meta: meta,
                index: index,
                easingValue: easingValue
            };

            if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
                return;
            }

            meta.controller.draw(easingValue);

            core_plugins.notify(me, 'afterDatasetDraw', [args]);
        },

        /**
         * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
         * hook, in which case, plugins will not be called on `afterTooltipDraw`.
         * @private
         */
        _drawTooltip: function (easingValue) {
            var me = this;
            var tooltip = me.tooltip;
            var args = {
                tooltip: tooltip,
                easingValue: easingValue
            };

            if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
                return;
            }

            tooltip.draw();

            core_plugins.notify(me, 'afterTooltipDraw', [args]);
        },

        /**
         * Get the single element that was clicked on
         * @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
         */
        getElementAtEvent: function (e) {
            return core_interaction.modes.single(this, e);
        },

        getElementsAtEvent: function (e) {
            return core_interaction.modes.label(this, e, { intersect: true });
        },

        getElementsAtXAxis: function (e) {
            return core_interaction.modes['x-axis'](this, e, { intersect: true });
        },

        getElementsAtEventForMode: function (e, mode, options) {
            var method = core_interaction.modes[mode];
            if (typeof method === 'function') {
                return method(this, e, options);
            }

            return [];
        },

        getDatasetAtEvent: function (e) {
            return core_interaction.modes.dataset(this, e, { intersect: true });
        },

        getDatasetMeta: function (datasetIndex) {
            var me = this;
            var dataset = me.data.datasets[datasetIndex];
            if (!dataset._meta) {
                dataset._meta = {};
            }

            var meta = dataset._meta[me.id];
            if (!meta) {
                meta = dataset._meta[me.id] = {
                    type: null,
                    data: [],
                    dataset: null,
                    controller: null,
                    hidden: null,			// See isDatasetVisible() comment
                    xAxisID: null,
                    yAxisID: null
                };
            }

            return meta;
        },

        getVisibleDatasetCount: function () {
            var count = 0;
            for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
                if (this.isDatasetVisible(i)) {
                    count++;
                }
            }
            return count;
        },

        isDatasetVisible: function (datasetIndex) {
            var meta = this.getDatasetMeta(datasetIndex);

            // meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
            // the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
            return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
        },

        generateLegend: function () {
            return this.options.legendCallback(this);
        },

        /**
         * @private
         */
        destroyDatasetMeta: function (datasetIndex) {
            var id = this.id;
            var dataset = this.data.datasets[datasetIndex];
            var meta = dataset._meta && dataset._meta[id];

            if (meta) {
                meta.controller.destroy();
                delete dataset._meta[id];
            }
        },

        destroy: function () {
            var me = this;
            var canvas = me.canvas;
            var i, ilen;

            me.stop();

            // dataset controllers need to cleanup associated data
            for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
                me.destroyDatasetMeta(i);
            }

            if (canvas) {
                me.unbindEvents();
                helpers$1.canvas.clear(me);
                platform.releaseContext(me.ctx);
                me.canvas = null;
                me.ctx = null;
            }

            core_plugins.notify(me, 'destroy');

            delete Chart.instances[me.id];
        },

        toBase64Image: function () {
            return this.canvas.toDataURL.apply(this.canvas, arguments);
        },

        initToolTip: function () {
            var me = this;
            me.tooltip = new core_tooltip({
                _chart: me,
                _chartInstance: me, // deprecated, backward compatibility
                _data: me.data,
                _options: me.options.tooltips
            }, me);
        },

        /**
         * @private
         */
        bindEvents: function () {
            var me = this;
            var listeners = me._listeners = {};
            var listener = function () {
                me.eventHandler.apply(me, arguments);
            };

            helpers$1.each(me.options.events, function (type) {
                platform.addEventListener(me, type, listener);
                listeners[type] = listener;
            });

            // Elements used to detect size change should not be injected for non responsive charts.
            // See https://github.com/chartjs/Chart.js/issues/2210
            if (me.options.responsive) {
                listener = function () {
                    me.resize();
                };

                platform.addEventListener(me, 'resize', listener);
                listeners.resize = listener;
            }
        },

        /**
         * @private
         */
        unbindEvents: function () {
            var me = this;
            var listeners = me._listeners;
            if (!listeners) {
                return;
            }

            delete me._listeners;
            helpers$1.each(listeners, function (listener, type) {
                platform.removeEventListener(me, type, listener);
            });
        },

        updateHoverStyle: function (elements, mode, enabled) {
            var method = enabled ? 'setHoverStyle' : 'removeHoverStyle';
            var element, i, ilen;

            for (i = 0, ilen = elements.length; i < ilen; ++i) {
                element = elements[i];
                if (element) {
                    this.getDatasetMeta(element._datasetIndex).controller[method](element);
                }
            }
        },

        /**
         * @private
         */
        eventHandler: function (e) {
            var me = this;
            var tooltip = me.tooltip;

            if (core_plugins.notify(me, 'beforeEvent', [e]) === false) {
                return;
            }

            // Buffer any update calls so that renders do not occur
            me._bufferedRender = true;
            me._bufferedRequest = null;

            var changed = me.handleEvent(e);
            // for smooth tooltip animations issue #4989
            // the tooltip should be the source of change
            // Animation check workaround:
            // tooltip._start will be null when tooltip isn't animating
            if (tooltip) {
                changed = tooltip._start
                    ? tooltip.handleEvent(e)
                    : changed | tooltip.handleEvent(e);
            }

            core_plugins.notify(me, 'afterEvent', [e]);

            var bufferedRequest = me._bufferedRequest;
            if (bufferedRequest) {
                // If we have an update that was triggered, we need to do a normal render
                me.render(bufferedRequest);
            } else if (changed && !me.animating) {
                // If entering, leaving, or changing elements, animate the change via pivot
                me.stop();

                // We only need to render at this point. Updating will cause scales to be
                // recomputed generating flicker & using more memory than necessary.
                me.render({
                    duration: me.options.hover.animationDuration,
                    lazy: true
                });
            }

            me._bufferedRender = false;
            me._bufferedRequest = null;

            return me;
        },

        /**
         * Handle an event
         * @private
         * @param {IEvent} event the event to handle
         * @return {boolean} true if the chart needs to re-render
         */
        handleEvent: function (e) {
            var me = this;
            var options = me.options || {};
            var hoverOptions = options.hover;
            var changed = false;

            me.lastActive = me.lastActive || [];

            // Find Active Elements for hover and tooltips
            if (e.type === 'mouseout') {
                me.active = [];
            } else {
                me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
            }

            // Invoke onHover hook
            // Need to call with native event here to not break backwards compatibility
            helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);

            if (e.type === 'mouseup' || e.type === 'click') {
                if (options.onClick) {
                    // Use e.native here for backwards compatibility
                    options.onClick.call(me, e.native, me.active);
                }
            }

            // Remove styling for last active (even if it may still be active)
            if (me.lastActive.length) {
                me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
            }

            // Built in hover styling
            if (me.active.length && hoverOptions.mode) {
                me.updateHoverStyle(me.active, hoverOptions.mode, true);
            }

            changed = !helpers$1.arrayEquals(me.active, me.lastActive);

            // Remember Last Actives
            me.lastActive = me.active;

            return changed;
        }
    });

    /**
     * NOTE(SB) We actually don't use this container anymore but we need to keep it
     * for backward compatibility. Though, it can still be useful for plugins that
     * would need to work on multiple charts?!
     */
    Chart.instances = {};

    var core_controller = Chart;

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, use Chart instead.
     * @class Chart.Controller
     * @deprecated since version 2.6
     * @todo remove at version 3
     * @private
     */
    Chart.Controller = Chart;

    /**
     * Provided for backward compatibility, not available anymore.
     * @namespace Chart
     * @deprecated since version 2.8
     * @todo remove at version 3
     * @private
     */
    Chart.types = {};

    /**
     * Provided for backward compatibility, not available anymore.
     * @namespace Chart.helpers.configMerge
     * @deprecated since version 2.8.0
     * @todo remove at version 3
     * @private
     */
    helpers$1.configMerge = mergeConfig;

    /**
     * Provided for backward compatibility, not available anymore.
     * @namespace Chart.helpers.scaleMerge
     * @deprecated since version 2.8.0
     * @todo remove at version 3
     * @private
     */
    helpers$1.scaleMerge = mergeScaleConfig;

    var core_helpers = function () {

        // -- Basic js utility methods

        helpers$1.where = function (collection, filterCallback) {
            if (helpers$1.isArray(collection) && Array.prototype.filter) {
                return collection.filter(filterCallback);
            }
            var filtered = [];

            helpers$1.each(collection, function (item) {
                if (filterCallback(item)) {
                    filtered.push(item);
                }
            });

            return filtered;
        };
        helpers$1.findIndex = Array.prototype.findIndex ?
            function (array, callback, scope) {
                return array.findIndex(callback, scope);
            } :
            function (array, callback, scope) {
                scope = scope === undefined ? array : scope;
                for (var i = 0, ilen = array.length; i < ilen; ++i) {
                    if (callback.call(scope, array[i], i, array)) {
                        return i;
                    }
                }
                return -1;
            };
        helpers$1.findNextWhere = function (arrayToSearch, filterCallback, startIndex) {
            // Default to start of the array
            if (helpers$1.isNullOrUndef(startIndex)) {
                startIndex = -1;
            }
            for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
                var currentItem = arrayToSearch[i];
                if (filterCallback(currentItem)) {
                    return currentItem;
                }
            }
        };
        helpers$1.findPreviousWhere = function (arrayToSearch, filterCallback, startIndex) {
            // Default to end of the array
            if (helpers$1.isNullOrUndef(startIndex)) {
                startIndex = arrayToSearch.length;
            }
            for (var i = startIndex - 1; i >= 0; i--) {
                var currentItem = arrayToSearch[i];
                if (filterCallback(currentItem)) {
                    return currentItem;
                }
            }
        };

        // -- Math methods
        helpers$1.isNumber = function (n) {
            return !isNaN(parseFloat(n)) && isFinite(n);
        };
        helpers$1.almostEquals = function (x, y, epsilon) {
            return Math.abs(x - y) < epsilon;
        };
        helpers$1.almostWhole = function (x, epsilon) {
            var rounded = Math.round(x);
            return (((rounded - epsilon) < x) && ((rounded + epsilon) > x));
        };
        helpers$1.max = function (array) {
            return array.reduce(function (max, value) {
                if (!isNaN(value)) {
                    return Math.max(max, value);
                }
                return max;
            }, Number.NEGATIVE_INFINITY);
        };
        helpers$1.min = function (array) {
            return array.reduce(function (min, value) {
                if (!isNaN(value)) {
                    return Math.min(min, value);
                }
                return min;
            }, Number.POSITIVE_INFINITY);
        };
        helpers$1.sign = Math.sign ?
            function (x) {
                return Math.sign(x);
            } :
            function (x) {
                x = +x; // convert to a number
                if (x === 0 || isNaN(x)) {
                    return x;
                }
                return x > 0 ? 1 : -1;
            };
        helpers$1.log10 = Math.log10 ?
            function (x) {
                return Math.log10(x);
            } :
            function (x) {
                var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
                // Check for whole powers of 10,
                // which due to floating point rounding error should be corrected.
                var powerOf10 = Math.round(exponent);
                var isPowerOf10 = x === Math.pow(10, powerOf10);

                return isPowerOf10 ? powerOf10 : exponent;
            };
        helpers$1.toRadians = function (degrees) {
            return degrees * (Math.PI / 180);
        };
        helpers$1.toDegrees = function (radians) {
            return radians * (180 / Math.PI);
        };

        /**
         * Returns the number of decimal places
         * i.e. the number of digits after the decimal point, of the value of this Number.
         * @param {number} x - A number.
         * @returns {number} The number of decimal places.
         * @private
         */
        helpers$1._decimalPlaces = function (x) {
            if (!helpers$1.isFinite(x)) {
                return;
            }
            var e = 1;
            var p = 0;
            while (Math.round(x * e) / e !== x) {
                e *= 10;
                p++;
            }
            return p;
        };

        // Gets the angle from vertical upright to the point about a centre.
        helpers$1.getAngleFromPoint = function (centrePoint, anglePoint) {
            var distanceFromXCenter = anglePoint.x - centrePoint.x;
            var distanceFromYCenter = anglePoint.y - centrePoint.y;
            var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);

            var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);

            if (angle < (-0.5 * Math.PI)) {
                angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
            }

            return {
                angle: angle,
                distance: radialDistanceFromCenter
            };
        };
        helpers$1.distanceBetweenPoints = function (pt1, pt2) {
            return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
        };

        /**
         * Provided for backward compatibility, not available anymore
         * @function Chart.helpers.aliasPixel
         * @deprecated since version 2.8.0
         * @todo remove at version 3
         */
        helpers$1.aliasPixel = function (pixelWidth) {
            return (pixelWidth % 2 === 0) ? 0 : 0.5;
        };

        /**
         * Returns the aligned pixel value to avoid anti-aliasing blur
         * @param {Chart} chart - The chart instance.
         * @param {number} pixel - A pixel value.
         * @param {number} width - The width of the element.
         * @returns {number} The aligned pixel value.
         * @private
         */
        helpers$1._alignPixel = function (chart, pixel, width) {
            var devicePixelRatio = chart.currentDevicePixelRatio;
            var halfWidth = width / 2;
            return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
        };

        helpers$1.splineCurve = function (firstPoint, middlePoint, afterPoint, t) {
            // Props to Rob Spencer at scaled innovation for his post on splining between points
            // http://scaledinnovation.com/analytics/splines/aboutSplines.html

            // This function must also respect "skipped" points

            var previous = firstPoint.skip ? middlePoint : firstPoint;
            var current = middlePoint;
            var next = afterPoint.skip ? middlePoint : afterPoint;

            var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
            var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));

            var s01 = d01 / (d01 + d12);
            var s12 = d12 / (d01 + d12);

            // If all points are the same, s01 & s02 will be inf
            s01 = isNaN(s01) ? 0 : s01;
            s12 = isNaN(s12) ? 0 : s12;

            var fa = t * s01; // scaling factor for triangle Ta
            var fb = t * s12;

            return {
                previous: {
                    x: current.x - fa * (next.x - previous.x),
                    y: current.y - fa * (next.y - previous.y)
                },
                next: {
                    x: current.x + fb * (next.x - previous.x),
                    y: current.y + fb * (next.y - previous.y)
                }
            };
        };
        helpers$1.EPSILON = Number.EPSILON || 1e-14;
        helpers$1.splineCurveMonotone = function (points) {
            // This function calculates B�zier control points in a similar way than |splineCurve|,
            // but preserves monotonicity of the provided data and ensures no local extremums are added
            // between the dataset discrete points due to the interpolation.
            // See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation

            var pointsWithTangents = (points || []).map(function (point) {
                return {
                    model: point._model,
                    deltaK: 0,
                    mK: 0
                };
            });

            // Calculate slopes (deltaK) and initialize tangents (mK)
            var pointsLen = pointsWithTangents.length;
            var i, pointBefore, pointCurrent, pointAfter;
            for (i = 0; i < pointsLen; ++i) {
                pointCurrent = pointsWithTangents[i];
                if (pointCurrent.model.skip) {
                    continue;
                }

                pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
                pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
                if (pointAfter && !pointAfter.model.skip) {
                    var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);

                    // In the case of two points that appear at the same x pixel, slopeDeltaX is 0
                    pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
                }

                if (!pointBefore || pointBefore.model.skip) {
                    pointCurrent.mK = pointCurrent.deltaK;
                } else if (!pointAfter || pointAfter.model.skip) {
                    pointCurrent.mK = pointBefore.deltaK;
                } else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
                    pointCurrent.mK = 0;
                } else {
                    pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
                }
            }

            // Adjust tangents to ensure monotonic properties
            var alphaK, betaK, tauK, squaredMagnitude;
            for (i = 0; i < pointsLen - 1; ++i) {
                pointCurrent = pointsWithTangents[i];
                pointAfter = pointsWithTangents[i + 1];
                if (pointCurrent.model.skip || pointAfter.model.skip) {
                    continue;
                }

                if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
                    pointCurrent.mK = pointAfter.mK = 0;
                    continue;
                }

                alphaK = pointCurrent.mK / pointCurrent.deltaK;
                betaK = pointAfter.mK / pointCurrent.deltaK;
                squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
                if (squaredMagnitude <= 9) {
                    continue;
                }

                tauK = 3 / Math.sqrt(squaredMagnitude);
                pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
                pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
            }

            // Compute control points
            var deltaX;
            for (i = 0; i < pointsLen; ++i) {
                pointCurrent = pointsWithTangents[i];
                if (pointCurrent.model.skip) {
                    continue;
                }

                pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
                pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
                if (pointBefore && !pointBefore.model.skip) {
                    deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
                    pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
                    pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
                }
                if (pointAfter && !pointAfter.model.skip) {
                    deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
                    pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
                    pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
                }
            }
        };
        helpers$1.nextItem = function (collection, index, loop) {
            if (loop) {
                return index >= collection.length - 1 ? collection[0] : collection[index + 1];
            }
            return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
        };
        helpers$1.previousItem = function (collection, index, loop) {
            if (loop) {
                return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
            }
            return index <= 0 ? collection[0] : collection[index - 1];
        };
        // Implementation of the nice number algorithm used in determining where axis labels will go
        helpers$1.niceNum = function (range, round) {
            var exponent = Math.floor(helpers$1.log10(range));
            var fraction = range / Math.pow(10, exponent);
            var niceFraction;

            if (round) {
                if (fraction < 1.5) {
                    niceFraction = 1;
                } else if (fraction < 3) {
                    niceFraction = 2;
                } else if (fraction < 7) {
                    niceFraction = 5;
                } else {
                    niceFraction = 10;
                }
            } else if (fraction <= 1.0) {
                niceFraction = 1;
            } else if (fraction <= 2) {
                niceFraction = 2;
            } else if (fraction <= 5) {
                niceFraction = 5;
            } else {
                niceFraction = 10;
            }

            return niceFraction * Math.pow(10, exponent);
        };
        // Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
        helpers$1.requestAnimFrame = (function () {
            if (typeof window === 'undefined') {
                return function (callback) {
                    callback();
                };
            }
            return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function (callback) {
                    return window.setTimeout(callback, 1000 / 60);
                };
        }());
        // -- DOM methods
        helpers$1.getRelativePosition = function (evt, chart) {
            var mouseX, mouseY;
            var e = evt.originalEvent || evt;
            var canvas = evt.target || evt.srcElement;
            var boundingRect = canvas.getBoundingClientRect();

            var touches = e.touches;
            if (touches && touches.length > 0) {
                mouseX = touches[0].clientX;
                mouseY = touches[0].clientY;

            } else {
                mouseX = e.clientX;
                mouseY = e.clientY;
            }

            // Scale mouse coordinates into canvas coordinates
            // by following the pattern laid out by 'jerryj' in the comments of
            // https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
            var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left'));
            var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top'));
            var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right'));
            var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom'));
            var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
            var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;

            // We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
            // the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
            mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
            mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);

            return {
                x: mouseX,
                y: mouseY
            };

        };

        // Private helper function to convert max-width/max-height values that may be percentages into a number
        function parseMaxStyle(styleValue, node, parentProperty) {
            var valueInPixels;
            if (typeof styleValue === 'string') {
                valueInPixels = parseInt(styleValue, 10);

                if (styleValue.indexOf('%') !== -1) {
                    // percentage * size in dimension
                    valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
                }
            } else {
                valueInPixels = styleValue;
            }

            return valueInPixels;
        }

        /**
         * Returns if the given value contains an effective constraint.
         * @private
         */
        function isConstrainedValue(value) {
            return value !== undefined && value !== null && value !== 'none';
        }

        /**
         * Returns the max width or height of the given DOM node in a cross-browser compatible fashion
         * @param {HTMLElement} domNode - the node to check the constraint on
         * @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height')
         * @param {string} percentageProperty - property of parent to use when calculating width as a percentage
         * @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser}
         */
        function getConstraintDimension(domNode, maxStyle, percentageProperty) {
            var view = document.defaultView;
            var parentNode = helpers$1._getParentNode(domNode);
            var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
            var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
            var hasCNode = isConstrainedValue(constrainedNode);
            var hasCContainer = isConstrainedValue(constrainedContainer);
            var infinity = Number.POSITIVE_INFINITY;

            if (hasCNode || hasCContainer) {
                return Math.min(
                    hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
                    hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
            }

            return 'none';
        }
        // returns Number or undefined if no constraint
        helpers$1.getConstraintWidth = function (domNode) {
            return getConstraintDimension(domNode, 'max-width', 'clientWidth');
        };
        // returns Number or undefined if no constraint
        helpers$1.getConstraintHeight = function (domNode) {
            return getConstraintDimension(domNode, 'max-height', 'clientHeight');
        };
        /**
         * @private
           */
        helpers$1._calculatePadding = function (container, padding, parentDimension) {
            padding = helpers$1.getStyle(container, padding);

            return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10);
        };
        /**
         * @private
         */
        helpers$1._getParentNode = function (domNode) {
            var parent = domNode.parentNode;
            if (parent && parent.toString() === '[object ShadowRoot]') {
                parent = parent.host;
            }
            return parent;
        };
        helpers$1.getMaximumWidth = function (domNode) {
            var container = helpers$1._getParentNode(domNode);
            if (!container) {
                return domNode.clientWidth;
            }

            var clientWidth = container.clientWidth;
            var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth);
            var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth);

            var w = clientWidth - paddingLeft - paddingRight;
            var cw = helpers$1.getConstraintWidth(domNode);
            return isNaN(cw) ? w : Math.min(w, cw);
        };
        helpers$1.getMaximumHeight = function (domNode) {
            var container = helpers$1._getParentNode(domNode);
            if (!container) {
                return domNode.clientHeight;
            }

            var clientHeight = container.clientHeight;
            var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight);
            var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight);

            var h = clientHeight - paddingTop - paddingBottom;
            var ch = helpers$1.getConstraintHeight(domNode);
            return isNaN(ch) ? h : Math.min(h, ch);
        };
        helpers$1.getStyle = function (el, property) {
            return el.currentStyle ?
                el.currentStyle[property] :
                document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
        };
        helpers$1.retinaScale = function (chart, forceRatio) {
            var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
            if (pixelRatio === 1) {
                return;
            }

            var canvas = chart.canvas;
            var height = chart.height;
            var width = chart.width;

            canvas.height = height * pixelRatio;
            canvas.width = width * pixelRatio;
            chart.ctx.scale(pixelRatio, pixelRatio);

            // If no style has been set on the canvas, the render size is used as display size,
            // making the chart visually bigger, so let's enforce it to the "correct" values.
            // See https://github.com/chartjs/Chart.js/issues/3575
            if (!canvas.style.height && !canvas.style.width) {
                canvas.style.height = height + 'px';
                canvas.style.width = width + 'px';
            }
        };
        // -- Canvas methods
        helpers$1.fontString = function (pixelSize, fontStyle, fontFamily) {
            return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
        };
        helpers$1.longestText = function (ctx, font, arrayOfThings, cache) {
            cache = cache || {};
            var data = cache.data = cache.data || {};
            var gc = cache.garbageCollect = cache.garbageCollect || [];

            if (cache.font !== font) {
                data = cache.data = {};
                gc = cache.garbageCollect = [];
                cache.font = font;
            }

            ctx.font = font;
            var longest = 0;
            helpers$1.each(arrayOfThings, function (thing) {
                // Undefined strings and arrays should not be measured
                if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) {
                    longest = helpers$1.measureText(ctx, data, gc, longest, thing);
                } else if (helpers$1.isArray(thing)) {
                    // if it is an array lets measure each element
                    // to do maybe simplify this function a bit so we can do this more recursively?
                    helpers$1.each(thing, function (nestedThing) {
                        // Undefined strings and arrays should not be measured
                        if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) {
                            longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing);
                        }
                    });
                }
            });

            var gcLen = gc.length / 2;
            if (gcLen > arrayOfThings.length) {
                for (var i = 0; i < gcLen; i++) {
                    delete data[gc[i]];
                }
                gc.splice(0, gcLen);
            }
            return longest;
        };
        helpers$1.measureText = function (ctx, data, gc, longest, string) {
            var textWidth = data[string];
            if (!textWidth) {
                textWidth = data[string] = ctx.measureText(string).width;
                gc.push(string);
            }
            if (textWidth > longest) {
                longest = textWidth;
            }
            return longest;
        };
        helpers$1.numberOfLabelLines = function (arrayOfThings) {
            var numberOfLines = 1;
            helpers$1.each(arrayOfThings, function (thing) {
                if (helpers$1.isArray(thing)) {
                    if (thing.length > numberOfLines) {
                        numberOfLines = thing.length;
                    }
                }
            });
            return numberOfLines;
        };

        helpers$1.color = !chartjsColor ?
            function (value) {
                console.error('Color.js not found!');
                return value;
            } :
            function (value) {
                /* global CanvasGradient */
                if (value instanceof CanvasGradient) {
                    value = core_defaults.global.defaultColor;
                }

                return chartjsColor(value);
            };

        helpers$1.getHoverColor = function (colorValue) {
            /* global CanvasPattern */
            return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ?
                colorValue :
                helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString();
        };
    };

    function abstract() {
        throw new Error(
            'This method is not implemented: either no adapter can ' +
            'be found or an incomplete integration was provided.'
        );
    }

    /**
     * Date adapter (current used by the time scale)
     * @namespace Chart._adapters._date
     * @memberof Chart._adapters
     * @private
     */

    /**
     * Currently supported unit string values.
     * @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')}
     * @memberof Chart._adapters._date
     * @name Unit
     */

    /**
     * @class
     */
    function DateAdapter(options) {
        this.options = options || {};
    }

    helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ {
        /**
         * Returns a map of time formats for the supported formatting units defined
         * in Unit as well as 'datetime' representing a detailed date/time string.
         * @returns {{string: string}}
         */
        formats: abstract,

        /**
         * Parses the given `value` and return the associated timestamp.
         * @param {any} value - the value to parse (usually comes from the data)
         * @param {string} [format] - the expected data format
         * @returns {(number|null)}
         * @function
         */
        parse: abstract,

        /**
         * Returns the formatted date in the specified `format` for a given `timestamp`.
         * @param {number} timestamp - the timestamp to format
         * @param {string} format - the date/time token
         * @return {string}
         * @function
         */
        format: abstract,

        /**
         * Adds the specified `amount` of `unit` to the given `timestamp`.
         * @param {number} timestamp - the input timestamp
         * @param {number} amount - the amount to add
         * @param {Unit} unit - the unit as string
         * @return {number}
         * @function
         */
        add: abstract,

        /**
         * Returns the number of `unit` between the given timestamps.
         * @param {number} max - the input timestamp (reference)
         * @param {number} min - the timestamp to substract
         * @param {Unit} unit - the unit as string
         * @return {number}
         * @function
         */
        diff: abstract,

        /**
         * Returns start of `unit` for the given `timestamp`.
         * @param {number} timestamp - the input timestamp
         * @param {Unit} unit - the unit as string
         * @param {number} [weekday] - the ISO day of the week with 1 being Monday
         * and 7 being Sunday (only needed if param *unit* is `isoWeek`).
         * @function
         */
        startOf: abstract,

        /**
         * Returns end of `unit` for the given `timestamp`.
         * @param {number} timestamp - the input timestamp
         * @param {Unit} unit - the unit as string
         * @function
         */
        endOf: abstract,

        // DEPRECATIONS

        /**
         * Provided for backward compatibility for scale.getValueForPixel(),
         * this method should be overridden only by the moment adapter.
         * @deprecated since version 2.8.0
         * @todo remove at version 3
         * @private
         */
        _create: function (value) {
            return value;
        }
    });

    DateAdapter.override = function (members) {
        helpers$1.extend(DateAdapter.prototype, members);
    };

    var _date = DateAdapter;

    var core_adapters = {
        _date: _date
    };

    /**
     * Namespace to hold static tick generation functions
     * @namespace Chart.Ticks
     */
    var core_ticks = {
        /**
         * Namespace to hold formatters for different types of ticks
         * @namespace Chart.Ticks.formatters
         */
        formatters: {
            /**
             * Formatter for value labels
             * @method Chart.Ticks.formatters.values
             * @param value the value to display
             * @return {string|string[]} the label to display
             */
            values: function (value) {
                return helpers$1.isArray(value) ? value : '' + value;
            },

            /**
             * Formatter for linear numeric ticks
             * @method Chart.Ticks.formatters.linear
             * @param tickValue {number} the value to be formatted
             * @param index {number} the position of the tickValue parameter in the ticks array
             * @param ticks {number[]} the list of ticks being converted
             * @return {string} string representation of the tickValue parameter
             */
            linear: function (tickValue, index, ticks) {
                // If we have lots of ticks, don't use the ones
                var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];

                // If we have a number like 2.5 as the delta, figure out how many decimal places we need
                if (Math.abs(delta) > 1) {
                    if (tickValue !== Math.floor(tickValue)) {
                        // not an integer
                        delta = tickValue - Math.floor(tickValue);
                    }
                }

                var logDelta = helpers$1.log10(Math.abs(delta));
                var tickString = '';

                if (tickValue !== 0) {
                    var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
                    if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
                        var logTick = helpers$1.log10(Math.abs(tickValue));
                        tickString = tickValue.toExponential(Math.floor(logTick) - Math.floor(logDelta));
                    } else {
                        var numDecimal = -1 * Math.floor(logDelta);
                        numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
                        tickString = tickValue.toFixed(numDecimal);
                    }
                } else {
                    tickString = '0'; // never show decimal places for 0
                }

                return tickString;
            },

            logarithmic: function (tickValue, index, ticks) {
                var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue))));

                if (tickValue === 0) {
                    return '0';
                } else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
                    return tickValue.toExponential();
                }
                return '';
            }
        }
    };

    var valueOrDefault$9 = helpers$1.valueOrDefault;
    var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault;

    core_defaults._set('scale', {
        display: true,
        position: 'left',
        offset: false,

        // grid line settings
        gridLines: {
            display: true,
            color: 'rgba(0, 0, 0, 0.1)',
            lineWidth: 1,
            drawBorder: true,
            drawOnChartArea: true,
            drawTicks: true,
            tickMarkLength: 10,
            zeroLineWidth: 1,
            zeroLineColor: 'rgba(0,0,0,0.25)',
            zeroLineBorderDash: [],
            zeroLineBorderDashOffset: 0.0,
            offsetGridLines: false,
            borderDash: [],
            borderDashOffset: 0.0
        },

        // scale label
        scaleLabel: {
            // display property
            display: false,

            // actual label
            labelString: '',

            // top/bottom padding
            padding: {
                top: 4,
                bottom: 4
            }
        },

        // label settings
        ticks: {
            beginAtZero: false,
            minRotation: 0,
            maxRotation: 50,
            mirror: false,
            padding: 0,
            reverse: false,
            display: true,
            autoSkip: true,
            autoSkipPadding: 0,
            labelOffset: 0,
            // We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
            callback: core_ticks.formatters.values,
            minor: {},
            major: {}
        }
    });

    function labelsFromTicks(ticks) {
        var labels = [];
        var i, ilen;

        for (i = 0, ilen = ticks.length; i < ilen; ++i) {
            labels.push(ticks[i].label);
        }

        return labels;
    }

    function getPixelForGridLine(scale, index, offsetGridLines) {
        var lineValue = scale.getPixelForTick(index);

        if (offsetGridLines) {
            if (scale.getTicks().length === 1) {
                lineValue -= scale.isHorizontal() ?
                    Math.max(lineValue - scale.left, scale.right - lineValue) :
                    Math.max(lineValue - scale.top, scale.bottom - lineValue);
            } else if (index === 0) {
                lineValue -= (scale.getPixelForTick(1) - lineValue) / 2;
            } else {
                lineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;
            }
        }
        return lineValue;
    }

    function computeTextSize(context, tick, font) {
        return helpers$1.isArray(tick) ?
            helpers$1.longestText(context, font, tick) :
            context.measureText(tick).width;
    }

    var core_scale = core_element.extend({
        /**
         * Get the padding needed for the scale
         * @method getPadding
         * @private
         * @returns {Padding} the necessary padding
         */
        getPadding: function () {
            var me = this;
            return {
                left: me.paddingLeft || 0,
                top: me.paddingTop || 0,
                right: me.paddingRight || 0,
                bottom: me.paddingBottom || 0
            };
        },

        /**
         * Returns the scale tick objects ({label, major})
         * @since 2.7
         */
        getTicks: function () {
            return this._ticks;
        },

        // These methods are ordered by lifecyle. Utilities then follow.
        // Any function defined here is inherited by all scale types.
        // Any function can be extended by the scale type

        mergeTicksOptions: function () {
            var ticks = this.options.ticks;
            if (ticks.minor === false) {
                ticks.minor = {
                    display: false
                };
            }
            if (ticks.major === false) {
                ticks.major = {
                    display: false
                };
            }
            for (var key in ticks) {
                if (key !== 'major' && key !== 'minor') {
                    if (typeof ticks.minor[key] === 'undefined') {
                        ticks.minor[key] = ticks[key];
                    }
                    if (typeof ticks.major[key] === 'undefined') {
                        ticks.major[key] = ticks[key];
                    }
                }
            }
        },
        beforeUpdate: function () {
            helpers$1.callback(this.options.beforeUpdate, [this]);
        },

        update: function (maxWidth, maxHeight, margins) {
            var me = this;
            var i, ilen, labels, label, ticks, tick;

            // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
            me.beforeUpdate();

            // Absorb the master measurements
            me.maxWidth = maxWidth;
            me.maxHeight = maxHeight;
            me.margins = helpers$1.extend({
                left: 0,
                right: 0,
                top: 0,
                bottom: 0
            }, margins);

            me._maxLabelLines = 0;
            me.longestLabelWidth = 0;
            me.longestTextCache = me.longestTextCache || {};

            // Dimensions
            me.beforeSetDimensions();
            me.setDimensions();
            me.afterSetDimensions();

            // Data min/max
            me.beforeDataLimits();
            me.determineDataLimits();
            me.afterDataLimits();

            // Ticks - `this.ticks` is now DEPRECATED!
            // Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
            // and must not be accessed directly from outside this class. `this.ticks` being
            // around for long time and not marked as private, we can't change its structure
            // without unexpected breaking changes. If you need to access the scale ticks,
            // use scale.getTicks() instead.

            me.beforeBuildTicks();

            // New implementations should return an array of objects but for BACKWARD COMPAT,
            // we still support no return (`this.ticks` internally set by calling this method).
            ticks = me.buildTicks() || [];

            // Allow modification of ticks in callback.
            ticks = me.afterBuildTicks(ticks) || ticks;

            me.beforeTickToLabelConversion();

            // New implementations should return the formatted tick labels but for BACKWARD
            // COMPAT, we still support no return (`this.ticks` internally changed by calling
            // this method and supposed to contain only string values).
            labels = me.convertTicksToLabels(ticks) || me.ticks;

            me.afterTickToLabelConversion();

            me.ticks = labels;   // BACKWARD COMPATIBILITY

            // IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!

            // BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
            for (i = 0, ilen = labels.length; i < ilen; ++i) {
                label = labels[i];
                tick = ticks[i];
                if (!tick) {
                    ticks.push(tick = {
                        label: label,
                        major: false
                    });
                } else {
                    tick.label = label;
                }
            }

            me._ticks = ticks;

            // Tick Rotation
            me.beforeCalculateTickRotation();
            me.calculateTickRotation();
            me.afterCalculateTickRotation();
            // Fit
            me.beforeFit();
            me.fit();
            me.afterFit();
            //
            me.afterUpdate();

            return me.minSize;

        },
        afterUpdate: function () {
            helpers$1.callback(this.options.afterUpdate, [this]);
        },

        //

        beforeSetDimensions: function () {
            helpers$1.callback(this.options.beforeSetDimensions, [this]);
        },
        setDimensions: function () {
            var me = this;
            // Set the unconstrained dimension before label rotation
            if (me.isHorizontal()) {
                // Reset position before calculating rotation
                me.width = me.maxWidth;
                me.left = 0;
                me.right = me.width;
            } else {
                me.height = me.maxHeight;

                // Reset position before calculating rotation
                me.top = 0;
                me.bottom = me.height;
            }

            // Reset padding
            me.paddingLeft = 0;
            me.paddingTop = 0;
            me.paddingRight = 0;
            me.paddingBottom = 0;
        },
        afterSetDimensions: function () {
            helpers$1.callback(this.options.afterSetDimensions, [this]);
        },

        // Data limits
        beforeDataLimits: function () {
            helpers$1.callback(this.options.beforeDataLimits, [this]);
        },
        determineDataLimits: helpers$1.noop,
        afterDataLimits: function () {
            helpers$1.callback(this.options.afterDataLimits, [this]);
        },

        //
        beforeBuildTicks: function () {
            helpers$1.callback(this.options.beforeBuildTicks, [this]);
        },
        buildTicks: helpers$1.noop,
        afterBuildTicks: function (ticks) {
            var me = this;
            // ticks is empty for old axis implementations here
            if (helpers$1.isArray(ticks) && ticks.length) {
                return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]);
            }
            // Support old implementations (that modified `this.ticks` directly in buildTicks)
            me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks;
            return ticks;
        },

        beforeTickToLabelConversion: function () {
            helpers$1.callback(this.options.beforeTickToLabelConversion, [this]);
        },
        convertTicksToLabels: function () {
            var me = this;
            // Convert ticks to strings
            var tickOpts = me.options.ticks;
            me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
        },
        afterTickToLabelConversion: function () {
            helpers$1.callback(this.options.afterTickToLabelConversion, [this]);
        },

        //

        beforeCalculateTickRotation: function () {
            helpers$1.callback(this.options.beforeCalculateTickRotation, [this]);
        },
        calculateTickRotation: function () {
            var me = this;
            var context = me.ctx;
            var tickOpts = me.options.ticks;
            var labels = labelsFromTicks(me._ticks);

            // Get the width of each grid by calculating the difference
            // between x offsets between 0 and 1.
            var tickFont = helpers$1.options._parseFont(tickOpts);
            context.font = tickFont.string;

            var labelRotation = tickOpts.minRotation || 0;

            if (labels.length && me.options.display && me.isHorizontal()) {
                var originalLabelWidth = helpers$1.longestText(context, tickFont.string, labels, me.longestTextCache);
                var labelWidth = originalLabelWidth;
                var cosRotation, sinRotation;

                // Allow 3 pixels x2 padding either side for label readability
                var tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;

                // Max label rotation can be set or default to 90 - also act as a loop counter
                while (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {
                    var angleRadians = helpers$1.toRadians(labelRotation);
                    cosRotation = Math.cos(angleRadians);
                    sinRotation = Math.sin(angleRadians);

                    if (sinRotation * originalLabelWidth > me.maxHeight) {
                        // go back one step
                        labelRotation--;
                        break;
                    }

                    labelRotation++;
                    labelWidth = cosRotation * originalLabelWidth;
                }
            }

            me.labelRotation = labelRotation;
        },
        afterCalculateTickRotation: function () {
            helpers$1.callback(this.options.afterCalculateTickRotation, [this]);
        },

        //

        beforeFit: function () {
            helpers$1.callback(this.options.beforeFit, [this]);
        },
        fit: function () {
            var me = this;
            // Reset
            var minSize = me.minSize = {
                width: 0,
                height: 0
            };

            var labels = labelsFromTicks(me._ticks);

            var opts = me.options;
            var tickOpts = opts.ticks;
            var scaleLabelOpts = opts.scaleLabel;
            var gridLineOpts = opts.gridLines;
            var display = me._isVisible();
            var position = opts.position;
            var isHorizontal = me.isHorizontal();

            var parseFont = helpers$1.options._parseFont;
            var tickFont = parseFont(tickOpts);
            var tickMarkLength = opts.gridLines.tickMarkLength;

            // Width
            if (isHorizontal) {
                // subtract the margins to line up with the chartArea if we are a full width scale
                minSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;
            } else {
                minSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
            }

            // height
            if (isHorizontal) {
                minSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;
            } else {
                minSize.height = me.maxHeight; // fill all the height
            }

            // Are we showing a title for the scale?
            if (scaleLabelOpts.display && display) {
                var scaleLabelFont = parseFont(scaleLabelOpts);
                var scaleLabelPadding = helpers$1.options.toPadding(scaleLabelOpts.padding);
                var deltaHeight = scaleLabelFont.lineHeight + scaleLabelPadding.height;

                if (isHorizontal) {
                    minSize.height += deltaHeight;
                } else {
                    minSize.width += deltaHeight;
                }
            }

            // Don't bother fitting the ticks if we are not showing the labels
            if (tickOpts.display && display) {
                var largestTextWidth = helpers$1.longestText(me.ctx, tickFont.string, labels, me.longestTextCache);
                var tallestLabelHeightInLines = helpers$1.numberOfLabelLines(labels);
                var lineSpace = tickFont.size * 0.5;
                var tickPadding = me.options.ticks.padding;

                // Store max number of lines and widest label for _autoSkip
                me._maxLabelLines = tallestLabelHeightInLines;
                me.longestLabelWidth = largestTextWidth;

                if (isHorizontal) {
                    var angleRadians = helpers$1.toRadians(me.labelRotation);
                    var cosRotation = Math.cos(angleRadians);
                    var sinRotation = Math.sin(angleRadians);

                    // TODO - improve this calculation
                    var labelHeight = (sinRotation * largestTextWidth)
                        + (tickFont.lineHeight * tallestLabelHeightInLines)
                        + lineSpace; // padding

                    minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);

                    me.ctx.font = tickFont.string;
                    var firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.string);
                    var lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.string);
                    var offsetLeft = me.getPixelForTick(0) - me.left;
                    var offsetRight = me.right - me.getPixelForTick(labels.length - 1);
                    var paddingLeft, paddingRight;

                    // Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
                    // which means that the right padding is dominated by the font height
                    if (me.labelRotation !== 0) {
                        paddingLeft = position === 'bottom' ? (cosRotation * firstLabelWidth) : (cosRotation * lineSpace);
                        paddingRight = position === 'bottom' ? (cosRotation * lineSpace) : (cosRotation * lastLabelWidth);
                    } else {
                        paddingLeft = firstLabelWidth / 2;
                        paddingRight = lastLabelWidth / 2;
                    }
                    me.paddingLeft = Math.max(paddingLeft - offsetLeft, 0) + 3; // add 3 px to move away from canvas edges
                    me.paddingRight = Math.max(paddingRight - offsetRight, 0) + 3;
                } else {
                    // A vertical axis is more constrained by the width. Labels are the
                    // dominant factor here, so get that length first and account for padding
                    if (tickOpts.mirror) {
                        largestTextWidth = 0;
                    } else {
                        // use lineSpace for consistency with horizontal axis
                        // tickPadding is not implemented for horizontal
                        largestTextWidth += tickPadding + lineSpace;
                    }

                    minSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);

                    me.paddingTop = tickFont.size / 2;
                    me.paddingBottom = tickFont.size / 2;
                }
            }

            me.handleMargins();

            me.width = minSize.width;
            me.height = minSize.height;
        },

        /**
         * Handle margins and padding interactions
         * @private
         */
        handleMargins: function () {
            var me = this;
            if (me.margins) {
                me.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);
                me.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);
                me.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);
                me.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);
            }
        },

        afterFit: function () {
            helpers$1.callback(this.options.afterFit, [this]);
        },

        // Shared Methods
        isHorizontal: function () {
            return this.options.position === 'top' || this.options.position === 'bottom';
        },
        isFullWidth: function () {
            return (this.options.fullWidth);
        },

        // Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
        getRightValue: function (rawValue) {
            // Null and undefined values first
            if (helpers$1.isNullOrUndef(rawValue)) {
                return NaN;
            }
            // isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
            if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) {
                return NaN;
            }
            // If it is in fact an object, dive in one more level
            if (rawValue) {
                if (this.isHorizontal()) {
                    if (rawValue.x !== undefined) {
                        return this.getRightValue(rawValue.x);
                    }
                } else if (rawValue.y !== undefined) {
                    return this.getRightValue(rawValue.y);
                }
            }

            // Value is good, return it
            return rawValue;
        },

        /**
         * Used to get the value to display in the tooltip for the data at the given index
         * @param index
         * @param datasetIndex
         */
        getLabelForIndex: helpers$1.noop,

        /**
         * Returns the location of the given data point. Value can either be an index or a numerical value
         * The coordinate (0, 0) is at the upper-left corner of the canvas
         * @param value
         * @param index
         * @param datasetIndex
         */
        getPixelForValue: helpers$1.noop,

        /**
         * Used to get the data value from a given pixel. This is the inverse of getPixelForValue
         * The coordinate (0, 0) is at the upper-left corner of the canvas
         * @param pixel
         */
        getValueForPixel: helpers$1.noop,

        /**
         * Returns the location of the tick at the given index
         * The coordinate (0, 0) is at the upper-left corner of the canvas
         */
        getPixelForTick: function (index) {
            var me = this;
            var offset = me.options.offset;
            if (me.isHorizontal()) {
                var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
                var tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
                var pixel = (tickWidth * index) + me.paddingLeft;

                if (offset) {
                    pixel += tickWidth / 2;
                }

                var finalVal = me.left + pixel;
                finalVal += me.isFullWidth() ? me.margins.left : 0;
                return finalVal;
            }
            var innerHeight = me.height - (me.paddingTop + me.paddingBottom);
            return me.top + (index * (innerHeight / (me._ticks.length - 1)));
        },

        /**
         * Utility for getting the pixel location of a percentage of scale
         * The coordinate (0, 0) is at the upper-left corner of the canvas
         */
        getPixelForDecimal: function (decimal) {
            var me = this;
            if (me.isHorizontal()) {
                var innerWidth = me.width - (me.paddingLeft + me.paddingRight);
                var valueOffset = (innerWidth * decimal) + me.paddingLeft;

                var finalVal = me.left + valueOffset;
                finalVal += me.isFullWidth() ? me.margins.left : 0;
                return finalVal;
            }
            return me.top + (decimal * me.height);
        },

        /**
         * Returns the pixel for the minimum chart value
         * The coordinate (0, 0) is at the upper-left corner of the canvas
         */
        getBasePixel: function () {
            return this.getPixelForValue(this.getBaseValue());
        },

        getBaseValue: function () {
            var me = this;
            var min = me.min;
            var max = me.max;

            return me.beginAtZero ? 0 :
                min < 0 && max < 0 ? max :
                    min > 0 && max > 0 ? min :
                        0;
        },

        /**
         * Returns a subset of ticks to be plotted to avoid overlapping labels.
         * @private
         */
        _autoSkip: function (ticks) {
            var me = this;
            var isHorizontal = me.isHorizontal();
            var optionTicks = me.options.ticks.minor;
            var tickCount = ticks.length;
            var skipRatio = false;
            var maxTicks = optionTicks.maxTicksLimit;

            // Total space needed to display all ticks. First and last ticks are
            // drawn as their center at end of axis, so tickCount-1
            var ticksLength = me._tickSize() * (tickCount - 1);

            // Axis length
            var axisLength = isHorizontal
                ? me.width - (me.paddingLeft + me.paddingRight)
                : me.height - (me.paddingTop + me.PaddingBottom);

            var result = [];
            var i, tick;

            if (ticksLength > axisLength) {
                skipRatio = 1 + Math.floor(ticksLength / axisLength);
            }

            // if they defined a max number of optionTicks,
            // increase skipRatio until that number is met
            if (tickCount > maxTicks) {
                skipRatio = Math.max(skipRatio, 1 + Math.floor(tickCount / maxTicks));
            }

            for (i = 0; i < tickCount; i++) {
                tick = ticks[i];

                if (skipRatio > 1 && i % skipRatio > 0) {
                    // leave tick in place but make sure it's not displayed (#4635)
                    delete tick.label;
                }
                result.push(tick);
            }
            return result;
        },

        /**
         * @private
         */
        _tickSize: function () {
            var me = this;
            var isHorizontal = me.isHorizontal();
            var optionTicks = me.options.ticks.minor;

            // Calculate space needed by label in axis direction.
            var rot = helpers$1.toRadians(me.labelRotation);
            var cos = Math.abs(Math.cos(rot));
            var sin = Math.abs(Math.sin(rot));

            var padding = optionTicks.autoSkipPadding || 0;
            var w = (me.longestLabelWidth + padding) || 0;

            var tickFont = helpers$1.options._parseFont(optionTicks);
            var h = (me._maxLabelLines * tickFont.lineHeight + padding) || 0;

            // Calculate space needed for 1 tick in axis direction.
            return isHorizontal
                ? h * cos > w * sin ? w / cos : h / sin
                : h * sin < w * cos ? h / cos : w / sin;
        },

        /**
         * @private
         */
        _isVisible: function () {
            var me = this;
            var chart = me.chart;
            var display = me.options.display;
            var i, ilen, meta;

            if (display !== 'auto') {
                return !!display;
            }

            // When 'auto', the scale is visible if at least one associated dataset is visible.
            for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
                if (chart.isDatasetVisible(i)) {
                    meta = chart.getDatasetMeta(i);
                    if (meta.xAxisID === me.id || meta.yAxisID === me.id) {
                        return true;
                    }
                }
            }

            return false;
        },

        /**
         * Actually draw the scale on the canvas
         * @param {object} chartArea - the area of the chart to draw full grid lines on
         */
        draw: function (chartArea) {
            var me = this;
            var options = me.options;

            if (!me._isVisible()) {
                return;
            }

            var chart = me.chart;
            var context = me.ctx;
            var globalDefaults = core_defaults.global;
            var defaultFontColor = globalDefaults.defaultFontColor;
            var optionTicks = options.ticks.minor;
            var optionMajorTicks = options.ticks.major || optionTicks;
            var gridLines = options.gridLines;
            var scaleLabel = options.scaleLabel;
            var position = options.position;

            var isRotated = me.labelRotation !== 0;
            var isMirrored = optionTicks.mirror;
            var isHorizontal = me.isHorizontal();

            var parseFont = helpers$1.options._parseFont;
            var ticks = optionTicks.display && optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();
            var tickFontColor = valueOrDefault$9(optionTicks.fontColor, defaultFontColor);
            var tickFont = parseFont(optionTicks);
            var lineHeight = tickFont.lineHeight;
            var majorTickFontColor = valueOrDefault$9(optionMajorTicks.fontColor, defaultFontColor);
            var majorTickFont = parseFont(optionMajorTicks);
            var tickPadding = optionTicks.padding;
            var labelOffset = optionTicks.labelOffset;

            var tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;

            var scaleLabelFontColor = valueOrDefault$9(scaleLabel.fontColor, defaultFontColor);
            var scaleLabelFont = parseFont(scaleLabel);
            var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding);
            var labelRotationRadians = helpers$1.toRadians(me.labelRotation);

            var itemsToDraw = [];

            var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
            var alignPixel = helpers$1._alignPixel;
            var borderValue, tickStart, tickEnd;

            if (position === 'top') {
                borderValue = alignPixel(chart, me.bottom, axisWidth);
                tickStart = me.bottom - tl;
                tickEnd = borderValue - axisWidth / 2;
            } else if (position === 'bottom') {
                borderValue = alignPixel(chart, me.top, axisWidth);
                tickStart = borderValue + axisWidth / 2;
                tickEnd = me.top + tl;
            } else if (position === 'left') {
                borderValue = alignPixel(chart, me.right, axisWidth);
                tickStart = me.right - tl;
                tickEnd = borderValue - axisWidth / 2;
            } else {
                borderValue = alignPixel(chart, me.left, axisWidth);
                tickStart = borderValue + axisWidth / 2;
                tickEnd = me.left + tl;
            }

            var epsilon = 0.0000001; // 0.0000001 is margin in pixels for Accumulated error.

            helpers$1.each(ticks, function (tick, index) {
                // autoskipper skipped this tick (#4635)
                if (helpers$1.isNullOrUndef(tick.label)) {
                    return;
                }

                var label = tick.label;
                var lineWidth, lineColor, borderDash, borderDashOffset;
                if (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {
                    // Draw the first index specially
                    lineWidth = gridLines.zeroLineWidth;
                    lineColor = gridLines.zeroLineColor;
                    borderDash = gridLines.zeroLineBorderDash || [];
                    borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0;
                } else {
                    lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, index);
                    lineColor = valueAtIndexOrDefault(gridLines.color, index);
                    borderDash = gridLines.borderDash || [];
                    borderDashOffset = gridLines.borderDashOffset || 0.0;
                }

                // Common properties
                var tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY, textOffset, textAlign;
                var labelCount = helpers$1.isArray(label) ? label.length : 1;
                var lineValue = getPixelForGridLine(me, index, gridLines.offsetGridLines);

                if (isHorizontal) {
                    var labelYOffset = tl + tickPadding;

                    if (lineValue < me.left - epsilon) {
                        lineColor = 'rgba(0,0,0,0)';
                    }

                    tx1 = tx2 = x1 = x2 = alignPixel(chart, lineValue, lineWidth);
                    ty1 = tickStart;
                    ty2 = tickEnd;
                    labelX = me.getPixelForTick(index) + labelOffset; // x values for optionTicks (need to consider offsetLabel option)

                    if (position === 'top') {
                        y1 = alignPixel(chart, chartArea.top, axisWidth) + axisWidth / 2;
                        y2 = chartArea.bottom;
                        textOffset = ((!isRotated ? 0.5 : 1) - labelCount) * lineHeight;
                        textAlign = !isRotated ? 'center' : 'left';
                        labelY = me.bottom - labelYOffset;
                    } else {
                        y1 = chartArea.top;
                        y2 = alignPixel(chart, chartArea.bottom, axisWidth) - axisWidth / 2;
                        textOffset = (!isRotated ? 0.5 : 0) * lineHeight;
                        textAlign = !isRotated ? 'center' : 'right';
                        labelY = me.top + labelYOffset;
                    }
                } else {
                    var labelXOffset = (isMirrored ? 0 : tl) + tickPadding;

                    if (lineValue < me.top - epsilon) {
                        lineColor = 'rgba(0,0,0,0)';
                    }

                    tx1 = tickStart;
                    tx2 = tickEnd;
                    ty1 = ty2 = y1 = y2 = alignPixel(chart, lineValue, lineWidth);
                    labelY = me.getPixelForTick(index) + labelOffset;
                    textOffset = (1 - labelCount) * lineHeight / 2;

                    if (position === 'left') {
                        x1 = alignPixel(chart, chartArea.left, axisWidth) + axisWidth / 2;
                        x2 = chartArea.right;
                        textAlign = isMirrored ? 'left' : 'right';
                        labelX = me.right - labelXOffset;
                    } else {
                        x1 = chartArea.left;
                        x2 = alignPixel(chart, chartArea.right, axisWidth) - axisWidth / 2;
                        textAlign = isMirrored ? 'right' : 'left';
                        labelX = me.left + labelXOffset;
                    }
                }

                itemsToDraw.push({
                    tx1: tx1,
                    ty1: ty1,
                    tx2: tx2,
                    ty2: ty2,
                    x1: x1,
                    y1: y1,
                    x2: x2,
                    y2: y2,
                    labelX: labelX,
                    labelY: labelY,
                    glWidth: lineWidth,
                    glColor: lineColor,
                    glBorderDash: borderDash,
                    glBorderDashOffset: borderDashOffset,
                    rotation: -1 * labelRotationRadians,
                    label: label,
                    major: tick.major,
                    textOffset: textOffset,
                    textAlign: textAlign
                });
            });

            // Draw all of the tick labels, tick marks, and grid lines at the correct places
            helpers$1.each(itemsToDraw, function (itemToDraw) {
                var glWidth = itemToDraw.glWidth;
                var glColor = itemToDraw.glColor;

                if (gridLines.display && glWidth && glColor) {
                    context.save();
                    context.lineWidth = glWidth;
                    context.strokeStyle = glColor;
                    if (context.setLineDash) {
                        context.setLineDash(itemToDraw.glBorderDash);
                        context.lineDashOffset = itemToDraw.glBorderDashOffset;
                    }

                    context.beginPath();

                    if (gridLines.drawTicks) {
                        context.moveTo(itemToDraw.tx1, itemToDraw.ty1);
                        context.lineTo(itemToDraw.tx2, itemToDraw.ty2);
                    }

                    if (gridLines.drawOnChartArea) {
                        context.moveTo(itemToDraw.x1, itemToDraw.y1);
                        context.lineTo(itemToDraw.x2, itemToDraw.y2);
                    }

                    context.stroke();
                    context.restore();
                }

                if (optionTicks.display) {
                    // Make sure we draw text in the correct color and font
                    context.save();
                    context.translate(itemToDraw.labelX, itemToDraw.labelY);
                    context.rotate(itemToDraw.rotation);
                    context.font = itemToDraw.major ? majorTickFont.string : tickFont.string;
                    context.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;
                    context.textBaseline = 'middle';
                    context.textAlign = itemToDraw.textAlign;

                    var label = itemToDraw.label;
                    var y = itemToDraw.textOffset;
                    if (helpers$1.isArray(label)) {
                        for (var i = 0; i < label.length; ++i) {
                            // We just make sure the multiline element is a string here..
                            context.fillText('' + label[i], 0, y);
                            y += lineHeight;
                        }
                    } else {
                        context.fillText(label, 0, y);
                    }
                    context.restore();
                }
            });

            if (scaleLabel.display) {
                // Draw the scale label
                var scaleLabelX;
                var scaleLabelY;
                var rotation = 0;
                var halfLineHeight = scaleLabelFont.lineHeight / 2;

                if (isHorizontal) {
                    scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
                    scaleLabelY = position === 'bottom'
                        ? me.bottom - halfLineHeight - scaleLabelPadding.bottom
                        : me.top + halfLineHeight + scaleLabelPadding.top;
                } else {
                    var isLeft = position === 'left';
                    scaleLabelX = isLeft
                        ? me.left + halfLineHeight + scaleLabelPadding.top
                        : me.right - halfLineHeight - scaleLabelPadding.top;
                    scaleLabelY = me.top + ((me.bottom - me.top) / 2);
                    rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
                }

                context.save();
                context.translate(scaleLabelX, scaleLabelY);
                context.rotate(rotation);
                context.textAlign = 'center';
                context.textBaseline = 'middle';
                context.fillStyle = scaleLabelFontColor; // render in correct colour
                context.font = scaleLabelFont.string;
                context.fillText(scaleLabel.labelString, 0, 0);
                context.restore();
            }

            if (axisWidth) {
                // Draw the line at the edge of the axis
                var firstLineWidth = axisWidth;
                var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, ticks.length - 1, 0);
                var x1, x2, y1, y2;

                if (isHorizontal) {
                    x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2;
                    x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2;
                    y1 = y2 = borderValue;
                } else {
                    y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2;
                    y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2;
                    x1 = x2 = borderValue;
                }

                context.lineWidth = axisWidth;
                context.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0);
                context.beginPath();
                context.moveTo(x1, y1);
                context.lineTo(x2, y2);
                context.stroke();
            }
        }
    });

    var defaultConfig = {
        position: 'bottom'
    };

    var scale_category = core_scale.extend({
        /**
        * Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those
        * else fall back to data.labels
        * @private
        */
        getLabels: function () {
            var data = this.chart.data;
            return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;
        },

        determineDataLimits: function () {
            var me = this;
            var labels = me.getLabels();
            me.minIndex = 0;
            me.maxIndex = labels.length - 1;
            var findIndex;

            if (me.options.ticks.min !== undefined) {
                // user specified min value
                findIndex = labels.indexOf(me.options.ticks.min);
                me.minIndex = findIndex !== -1 ? findIndex : me.minIndex;
            }

            if (me.options.ticks.max !== undefined) {
                // user specified max value
                findIndex = labels.indexOf(me.options.ticks.max);
                me.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;
            }

            me.min = labels[me.minIndex];
            me.max = labels[me.maxIndex];
        },

        buildTicks: function () {
            var me = this;
            var labels = me.getLabels();
            // If we are viewing some subset of labels, slice the original array
            me.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);
        },

        getLabelForIndex: function (index, datasetIndex) {
            var me = this;
            var chart = me.chart;

            if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) {
                return me.getRightValue(chart.data.datasets[datasetIndex].data[index]);
            }

            return me.ticks[index - me.minIndex];
        },

        // Used to get data value locations.  Value can either be an index or a numerical value
        getPixelForValue: function (value, index) {
            var me = this;
            var offset = me.options.offset;
            // 1 is added because we need the length but we have the indexes
            var offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);

            // If value is a data object, then index is the index in the data array,
            // not the index of the scale. We need to change that.
            var valueCategory;
            if (value !== undefined && value !== null) {
                valueCategory = me.isHorizontal() ? value.x : value.y;
            }
            if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
                var labels = me.getLabels();
                value = valueCategory || value;
                var idx = labels.indexOf(value);
                index = idx !== -1 ? idx : index;
            }

            if (me.isHorizontal()) {
                var valueWidth = me.width / offsetAmt;
                var widthOffset = (valueWidth * (index - me.minIndex));

                if (offset) {
                    widthOffset += (valueWidth / 2);
                }

                return me.left + widthOffset;
            }
            var valueHeight = me.height / offsetAmt;
            var heightOffset = (valueHeight * (index - me.minIndex));

            if (offset) {
                heightOffset += (valueHeight / 2);
            }

            return me.top + heightOffset;
        },

        getPixelForTick: function (index) {
            return this.getPixelForValue(this.ticks[index], index + this.minIndex, null);
        },

        getValueForPixel: function (pixel) {
            var me = this;
            var offset = me.options.offset;
            var value;
            var offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);
            var horz = me.isHorizontal();
            var valueDimension = (horz ? me.width : me.height) / offsetAmt;

            pixel -= horz ? me.left : me.top;

            if (offset) {
                pixel -= (valueDimension / 2);
            }

            if (pixel <= 0) {
                value = 0;
            } else {
                value = Math.round(pixel / valueDimension);
            }

            return value + me.minIndex;
        },

        getBasePixel: function () {
            return this.bottom;
        }
    });

    // INTERNAL: static default options, registered in src/index.js
    var _defaults = defaultConfig;
    scale_category._defaults = _defaults;

    var noop = helpers$1.noop;
    var isNullOrUndef = helpers$1.isNullOrUndef;

    /**
     * Generate a set of linear ticks
     * @param generationOptions the options used to generate the ticks
     * @param dataRange the range of the data
     * @returns {number[]} array of tick values
     */
    function generateTicks(generationOptions, dataRange) {
        var ticks = [];
        // To get a "nice" value for the tick spacing, we will use the appropriately named
        // "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
        // for details.

        var MIN_SPACING = 1e-14;
        var stepSize = generationOptions.stepSize;
        var unit = stepSize || 1;
        var maxNumSpaces = generationOptions.maxTicks - 1;
        var min = generationOptions.min;
        var max = generationOptions.max;
        var precision = generationOptions.precision;
        var rmin = dataRange.min;
        var rmax = dataRange.max;
        var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit;
        var factor, niceMin, niceMax, numSpaces;

        // Beyond MIN_SPACING floating point numbers being to lose precision
        // such that we can't do the math necessary to generate ticks
        if (spacing < MIN_SPACING && isNullOrUndef(min) && isNullOrUndef(max)) {
            return [rmin, rmax];
        }

        numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
        if (numSpaces > maxNumSpaces) {
            // If the calculated num of spaces exceeds maxNumSpaces, recalculate it
            spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit;
        }

        if (stepSize || isNullOrUndef(precision)) {
            // If a precision is not specified, calculate factor based on spacing
            factor = Math.pow(10, helpers$1._decimalPlaces(spacing));
        } else {
            // If the user specified a precision, round to that number of decimal places
            factor = Math.pow(10, precision);
            spacing = Math.ceil(spacing * factor) / factor;
        }

        niceMin = Math.floor(rmin / spacing) * spacing;
        niceMax = Math.ceil(rmax / spacing) * spacing;

        // If min, max and stepSize is set and they make an evenly spaced scale use it.
        if (stepSize) {
            // If very close to our whole number, use it.
            if (!isNullOrUndef(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) {
                niceMin = min;
            }
            if (!isNullOrUndef(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) {
                niceMax = max;
            }
        }

        numSpaces = (niceMax - niceMin) / spacing;
        // If very close to our rounded value, use it.
        if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
            numSpaces = Math.round(numSpaces);
        } else {
            numSpaces = Math.ceil(numSpaces);
        }

        niceMin = Math.round(niceMin * factor) / factor;
        niceMax = Math.round(niceMax * factor) / factor;
        ticks.push(isNullOrUndef(min) ? niceMin : min);
        for (var j = 1; j < numSpaces; ++j) {
            ticks.push(Math.round((niceMin + j * spacing) * factor) / factor);
        }
        ticks.push(isNullOrUndef(max) ? niceMax : max);

        return ticks;
    }

    var scale_linearbase = core_scale.extend({
        getRightValue: function (value) {
            if (typeof value === 'string') {
                return +value;
            }
            return core_scale.prototype.getRightValue.call(this, value);
        },

        handleTickRangeOptions: function () {
            var me = this;
            var opts = me.options;
            var tickOpts = opts.ticks;

            // If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
            // do nothing since that would make the chart weird. If the user really wants a weird chart
            // axis, they can manually override it
            if (tickOpts.beginAtZero) {
                var minSign = helpers$1.sign(me.min);
                var maxSign = helpers$1.sign(me.max);

                if (minSign < 0 && maxSign < 0) {
                    // move the top up to 0
                    me.max = 0;
                } else if (minSign > 0 && maxSign > 0) {
                    // move the bottom down to 0
                    me.min = 0;
                }
            }

            var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
            var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;

            if (tickOpts.min !== undefined) {
                me.min = tickOpts.min;
            } else if (tickOpts.suggestedMin !== undefined) {
                if (me.min === null) {
                    me.min = tickOpts.suggestedMin;
                } else {
                    me.min = Math.min(me.min, tickOpts.suggestedMin);
                }
            }

            if (tickOpts.max !== undefined) {
                me.max = tickOpts.max;
            } else if (tickOpts.suggestedMax !== undefined) {
                if (me.max === null) {
                    me.max = tickOpts.suggestedMax;
                } else {
                    me.max = Math.max(me.max, tickOpts.suggestedMax);
                }
            }

            if (setMin !== setMax) {
                // We set the min or the max but not both.
                // So ensure that our range is good
                // Inverted or 0 length range can happen when
                // ticks.min is set, and no datasets are visible
                if (me.min >= me.max) {
                    if (setMin) {
                        me.max = me.min + 1;
                    } else {
                        me.min = me.max - 1;
                    }
                }
            }

            if (me.min === me.max) {
                me.max++;

                if (!tickOpts.beginAtZero) {
                    me.min--;
                }
            }
        },

        getTickLimit: function () {
            var me = this;
            var tickOpts = me.options.ticks;
            var stepSize = tickOpts.stepSize;
            var maxTicksLimit = tickOpts.maxTicksLimit;
            var maxTicks;

            if (stepSize) {
                maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
            } else {
                maxTicks = me._computeTickLimit();
                maxTicksLimit = maxTicksLimit || 11;
            }

            if (maxTicksLimit) {
                maxTicks = Math.min(maxTicksLimit, maxTicks);
            }

            return maxTicks;
        },

        _computeTickLimit: function () {
            return Number.POSITIVE_INFINITY;
        },

        handleDirectionalChanges: noop,

        buildTicks: function () {
            var me = this;
            var opts = me.options;
            var tickOpts = opts.ticks;

            // Figure out what the max number of ticks we can support it is based on the size of
            // the axis area. For now, we say that the minimum tick spacing in pixels must be 40
            // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
            // the graph. Make sure we always have at least 2 ticks
            var maxTicks = me.getTickLimit();
            maxTicks = Math.max(2, maxTicks);

            var numericGeneratorOptions = {
                maxTicks: maxTicks,
                min: tickOpts.min,
                max: tickOpts.max,
                precision: tickOpts.precision,
                stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
            };
            var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);

            me.handleDirectionalChanges();

            // At this point, we need to update our max and min given the tick values since we have expanded the
            // range of the scale
            me.max = helpers$1.max(ticks);
            me.min = helpers$1.min(ticks);

            if (tickOpts.reverse) {
                ticks.reverse();

                me.start = me.max;
                me.end = me.min;
            } else {
                me.start = me.min;
                me.end = me.max;
            }
        },

        convertTicksToLabels: function () {
            var me = this;
            me.ticksAsNumbers = me.ticks.slice();
            me.zeroLineIndex = me.ticks.indexOf(0);

            core_scale.prototype.convertTicksToLabels.call(me);
        }
    });

    var defaultConfig$1 = {
        position: 'left',
        ticks: {
            callback: core_ticks.formatters.linear
        }
    };

    var scale_linear = scale_linearbase.extend({
        determineDataLimits: function () {
            var me = this;
            var opts = me.options;
            var chart = me.chart;
            var data = chart.data;
            var datasets = data.datasets;
            var isHorizontal = me.isHorizontal();
            var DEFAULT_MIN = 0;
            var DEFAULT_MAX = 1;

            function IDMatches(meta) {
                return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
            }

            // First Calculate the range
            me.min = null;
            me.max = null;

            var hasStacks = opts.stacked;
            if (hasStacks === undefined) {
                helpers$1.each(datasets, function (dataset, datasetIndex) {
                    if (hasStacks) {
                        return;
                    }

                    var meta = chart.getDatasetMeta(datasetIndex);
                    if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
                        meta.stack !== undefined) {
                        hasStacks = true;
                    }
                });
            }

            if (opts.stacked || hasStacks) {
                var valuesPerStack = {};

                helpers$1.each(datasets, function (dataset, datasetIndex) {
                    var meta = chart.getDatasetMeta(datasetIndex);
                    var key = [
                        meta.type,
                        // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
                        ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
                        meta.stack
                    ].join('.');

                    if (valuesPerStack[key] === undefined) {
                        valuesPerStack[key] = {
                            positiveValues: [],
                            negativeValues: []
                        };
                    }

                    // Store these per type
                    var positiveValues = valuesPerStack[key].positiveValues;
                    var negativeValues = valuesPerStack[key].negativeValues;

                    if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                        helpers$1.each(dataset.data, function (rawValue, index) {
                            var value = +me.getRightValue(rawValue);
                            if (isNaN(value) || meta.data[index].hidden) {
                                return;
                            }

                            positiveValues[index] = positiveValues[index] || 0;
                            negativeValues[index] = negativeValues[index] || 0;

                            if (opts.relativePoints) {
                                positiveValues[index] = 100;
                            } else if (value < 0) {
                                negativeValues[index] += value;
                            } else {
                                positiveValues[index] += value;
                            }
                        });
                    }
                });

                helpers$1.each(valuesPerStack, function (valuesForType) {
                    var values = valuesForType.positiveValues.concat(valuesForType.negativeValues);
                    var minVal = helpers$1.min(values);
                    var maxVal = helpers$1.max(values);
                    me.min = me.min === null ? minVal : Math.min(me.min, minVal);
                    me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
                });

            } else {
                helpers$1.each(datasets, function (dataset, datasetIndex) {
                    var meta = chart.getDatasetMeta(datasetIndex);
                    if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                        helpers$1.each(dataset.data, function (rawValue, index) {
                            var value = +me.getRightValue(rawValue);
                            if (isNaN(value) || meta.data[index].hidden) {
                                return;
                            }

                            if (me.min === null) {
                                me.min = value;
                            } else if (value < me.min) {
                                me.min = value;
                            }

                            if (me.max === null) {
                                me.max = value;
                            } else if (value > me.max) {
                                me.max = value;
                            }
                        });
                    }
                });
            }

            me.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
            me.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;

            // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
            this.handleTickRangeOptions();
        },

        // Returns the maximum number of ticks based on the scale dimension
        _computeTickLimit: function () {
            var me = this;
            var tickFont;

            if (me.isHorizontal()) {
                return Math.ceil(me.width / 40);
            }
            tickFont = helpers$1.options._parseFont(me.options.ticks);
            return Math.ceil(me.height / tickFont.lineHeight);
        },

        // Called after the ticks are built. We need
        handleDirectionalChanges: function () {
            if (!this.isHorizontal()) {
                // We are in a vertical orientation. The top value is the highest. So reverse the array
                this.ticks.reverse();
            }
        },

        getLabelForIndex: function (index, datasetIndex) {
            return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
        },

        // Utils
        getPixelForValue: function (value) {
            // This must be called after fit has been run so that
            // this.left, this.top, this.right, and this.bottom have been defined
            var me = this;
            var start = me.start;

            var rightValue = +me.getRightValue(value);
            var pixel;
            var range = me.end - start;

            if (me.isHorizontal()) {
                pixel = me.left + (me.width / range * (rightValue - start));
            } else {
                pixel = me.bottom - (me.height / range * (rightValue - start));
            }
            return pixel;
        },

        getValueForPixel: function (pixel) {
            var me = this;
            var isHorizontal = me.isHorizontal();
            var innerDimension = isHorizontal ? me.width : me.height;
            var offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;
            return me.start + ((me.end - me.start) * offset);
        },

        getPixelForTick: function (index) {
            return this.getPixelForValue(this.ticksAsNumbers[index]);
        }
    });

    // INTERNAL: static default options, registered in src/index.js
    var _defaults$1 = defaultConfig$1;
    scale_linear._defaults = _defaults$1;

    var valueOrDefault$a = helpers$1.valueOrDefault;

    /**
     * Generate a set of logarithmic ticks
     * @param generationOptions the options used to generate the ticks
     * @param dataRange the range of the data
     * @returns {number[]} array of tick values
     */
    function generateTicks$1(generationOptions, dataRange) {
        var ticks = [];

        var tickVal = valueOrDefault$a(generationOptions.min, Math.pow(10, Math.floor(helpers$1.log10(dataRange.min))));

        var endExp = Math.floor(helpers$1.log10(dataRange.max));
        var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
        var exp, significand;

        if (tickVal === 0) {
            exp = Math.floor(helpers$1.log10(dataRange.minNotZero));
            significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));

            ticks.push(tickVal);
            tickVal = significand * Math.pow(10, exp);
        } else {
            exp = Math.floor(helpers$1.log10(tickVal));
            significand = Math.floor(tickVal / Math.pow(10, exp));
        }
        var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;

        do {
            ticks.push(tickVal);

            ++significand;
            if (significand === 10) {
                significand = 1;
                ++exp;
                precision = exp >= 0 ? 1 : precision;
            }

            tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
        } while (exp < endExp || (exp === endExp && significand < endSignificand));

        var lastTick = valueOrDefault$a(generationOptions.max, tickVal);
        ticks.push(lastTick);

        return ticks;
    }

    var defaultConfig$2 = {
        position: 'left',

        // label settings
        ticks: {
            callback: core_ticks.formatters.logarithmic
        }
    };

    // TODO(v3): change this to positiveOrDefault
    function nonNegativeOrDefault(value, defaultValue) {
        return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue;
    }

    var scale_logarithmic = core_scale.extend({
        determineDataLimits: function () {
            var me = this;
            var opts = me.options;
            var chart = me.chart;
            var data = chart.data;
            var datasets = data.datasets;
            var isHorizontal = me.isHorizontal();
            function IDMatches(meta) {
                return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
            }

            // Calculate Range
            me.min = null;
            me.max = null;
            me.minNotZero = null;

            var hasStacks = opts.stacked;
            if (hasStacks === undefined) {
                helpers$1.each(datasets, function (dataset, datasetIndex) {
                    if (hasStacks) {
                        return;
                    }

                    var meta = chart.getDatasetMeta(datasetIndex);
                    if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
                        meta.stack !== undefined) {
                        hasStacks = true;
                    }
                });
            }

            if (opts.stacked || hasStacks) {
                var valuesPerStack = {};

                helpers$1.each(datasets, function (dataset, datasetIndex) {
                    var meta = chart.getDatasetMeta(datasetIndex);
                    var key = [
                        meta.type,
                        // we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
                        ((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
                        meta.stack
                    ].join('.');

                    if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                        if (valuesPerStack[key] === undefined) {
                            valuesPerStack[key] = [];
                        }

                        helpers$1.each(dataset.data, function (rawValue, index) {
                            var values = valuesPerStack[key];
                            var value = +me.getRightValue(rawValue);
                            // invalid, hidden and negative values are ignored
                            if (isNaN(value) || meta.data[index].hidden || value < 0) {
                                return;
                            }
                            values[index] = values[index] || 0;
                            values[index] += value;
                        });
                    }
                });

                helpers$1.each(valuesPerStack, function (valuesForType) {
                    if (valuesForType.length > 0) {
                        var minVal = helpers$1.min(valuesForType);
                        var maxVal = helpers$1.max(valuesForType);
                        me.min = me.min === null ? minVal : Math.min(me.min, minVal);
                        me.max = me.max === null ? maxVal : Math.max(me.max, maxVal);
                    }
                });

            } else {
                helpers$1.each(datasets, function (dataset, datasetIndex) {
                    var meta = chart.getDatasetMeta(datasetIndex);
                    if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
                        helpers$1.each(dataset.data, function (rawValue, index) {
                            var value = +me.getRightValue(rawValue);
                            // invalid, hidden and negative values are ignored
                            if (isNaN(value) || meta.data[index].hidden || value < 0) {
                                return;
                            }

                            if (me.min === null) {
                                me.min = value;
                            } else if (value < me.min) {
                                me.min = value;
                            }

                            if (me.max === null) {
                                me.max = value;
                            } else if (value > me.max) {
                                me.max = value;
                            }

                            if (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {
                                me.minNotZero = value;
                            }
                        });
                    }
                });
            }

            // Common base implementation to handle ticks.min, ticks.max
            this.handleTickRangeOptions();
        },

        handleTickRangeOptions: function () {
            var me = this;
            var tickOpts = me.options.ticks;
            var DEFAULT_MIN = 1;
            var DEFAULT_MAX = 10;

            me.min = nonNegativeOrDefault(tickOpts.min, me.min);
            me.max = nonNegativeOrDefault(tickOpts.max, me.max);

            if (me.min === me.max) {
                if (me.min !== 0 && me.min !== null) {
                    me.min = Math.pow(10, Math.floor(helpers$1.log10(me.min)) - 1);
                    me.max = Math.pow(10, Math.floor(helpers$1.log10(me.max)) + 1);
                } else {
                    me.min = DEFAULT_MIN;
                    me.max = DEFAULT_MAX;
                }
            }
            if (me.min === null) {
                me.min = Math.pow(10, Math.floor(helpers$1.log10(me.max)) - 1);
            }
            if (me.max === null) {
                me.max = me.min !== 0
                    ? Math.pow(10, Math.floor(helpers$1.log10(me.min)) + 1)
                    : DEFAULT_MAX;
            }
            if (me.minNotZero === null) {
                if (me.min > 0) {
                    me.minNotZero = me.min;
                } else if (me.max < 1) {
                    me.minNotZero = Math.pow(10, Math.floor(helpers$1.log10(me.max)));
                } else {
                    me.minNotZero = DEFAULT_MIN;
                }
            }
        },

        buildTicks: function () {
            var me = this;
            var tickOpts = me.options.ticks;
            var reverse = !me.isHorizontal();

            var generationOptions = {
                min: nonNegativeOrDefault(tickOpts.min),
                max: nonNegativeOrDefault(tickOpts.max)
            };
            var ticks = me.ticks = generateTicks$1(generationOptions, me);

            // At this point, we need to update our max and min given the tick values since we have expanded the
            // range of the scale
            me.max = helpers$1.max(ticks);
            me.min = helpers$1.min(ticks);

            if (tickOpts.reverse) {
                reverse = !reverse;
                me.start = me.max;
                me.end = me.min;
            } else {
                me.start = me.min;
                me.end = me.max;
            }
            if (reverse) {
                ticks.reverse();
            }
        },

        convertTicksToLabels: function () {
            this.tickValues = this.ticks.slice();

            core_scale.prototype.convertTicksToLabels.call(this);
        },

        // Get the correct tooltip label
        getLabelForIndex: function (index, datasetIndex) {
            return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
        },

        getPixelForTick: function (index) {
            return this.getPixelForValue(this.tickValues[index]);
        },

        /**
         * Returns the value of the first tick.
         * @param {number} value - The minimum not zero value.
         * @return {number} The first tick value.
         * @private
         */
        _getFirstTickValue: function (value) {
            var exp = Math.floor(helpers$1.log10(value));
            var significand = Math.floor(value / Math.pow(10, exp));

            return significand * Math.pow(10, exp);
        },

        getPixelForValue: function (value) {
            var me = this;
            var tickOpts = me.options.ticks;
            var reverse = tickOpts.reverse;
            var log10 = helpers$1.log10;
            var firstTickValue = me._getFirstTickValue(me.minNotZero);
            var offset = 0;
            var innerDimension, pixel, start, end, sign;

            value = +me.getRightValue(value);
            if (reverse) {
                start = me.end;
                end = me.start;
                sign = -1;
            } else {
                start = me.start;
                end = me.end;
                sign = 1;
            }
            if (me.isHorizontal()) {
                innerDimension = me.width;
                pixel = reverse ? me.right : me.left;
            } else {
                innerDimension = me.height;
                sign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)
                pixel = reverse ? me.top : me.bottom;
            }
            if (value !== start) {
                if (start === 0) { // include zero tick
                    offset = valueOrDefault$a(tickOpts.fontSize, core_defaults.global.defaultFontSize);
                    innerDimension -= offset;
                    start = firstTickValue;
                }
                if (value !== 0) {
                    offset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));
                }
                pixel += sign * offset;
            }
            return pixel;
        },

        getValueForPixel: function (pixel) {
            var me = this;
            var tickOpts = me.options.ticks;
            var reverse = tickOpts.reverse;
            var log10 = helpers$1.log10;
            var firstTickValue = me._getFirstTickValue(me.minNotZero);
            var innerDimension, start, end, value;

            if (reverse) {
                start = me.end;
                end = me.start;
            } else {
                start = me.start;
                end = me.end;
            }
            if (me.isHorizontal()) {
                innerDimension = me.width;
                value = reverse ? me.right - pixel : pixel - me.left;
            } else {
                innerDimension = me.height;
                value = reverse ? pixel - me.top : me.bottom - pixel;
            }
            if (value !== start) {
                if (start === 0) { // include zero tick
                    var offset = valueOrDefault$a(tickOpts.fontSize, core_defaults.global.defaultFontSize);
                    value -= offset;
                    innerDimension -= offset;
                    start = firstTickValue;
                }
                value *= log10(end) - log10(start);
                value /= innerDimension;
                value = Math.pow(10, log10(start) + value);
            }
            return value;
        }
    });

    // INTERNAL: static default options, registered in src/index.js
    var _defaults$2 = defaultConfig$2;
    scale_logarithmic._defaults = _defaults$2;

    var valueOrDefault$b = helpers$1.valueOrDefault;
    var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault;
    var resolve$7 = helpers$1.options.resolve;

    var defaultConfig$3 = {
        display: true,

        // Boolean - Whether to animate scaling the chart from the centre
        animate: true,
        position: 'chartArea',

        angleLines: {
            display: true,
            color: 'rgba(0, 0, 0, 0.1)',
            lineWidth: 1,
            borderDash: [],
            borderDashOffset: 0.0
        },

        gridLines: {
            circular: false
        },

        // label settings
        ticks: {
            // Boolean - Show a backdrop to the scale label
            showLabelBackdrop: true,

            // String - The colour of the label backdrop
            backdropColor: 'rgba(255,255,255,0.75)',

            // Number - The backdrop padding above & below the label in pixels
            backdropPaddingY: 2,

            // Number - The backdrop padding to the side of the label in pixels
            backdropPaddingX: 2,

            callback: core_ticks.formatters.linear
        },

        pointLabels: {
            // Boolean - if true, show point labels
            display: true,

            // Number - Point label font size in pixels
            fontSize: 10,

            // Function - Used to convert point labels
            callback: function (label) {
                return label;
            }
        }
    };

    function getValueCount(scale) {
        var opts = scale.options;
        return opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;
    }

    function getTickBackdropHeight(opts) {
        var tickOpts = opts.ticks;

        if (tickOpts.display && opts.display) {
            return valueOrDefault$b(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2;
        }
        return 0;
    }

    function measureLabelSize(ctx, lineHeight, label) {
        if (helpers$1.isArray(label)) {
            return {
                w: helpers$1.longestText(ctx, ctx.font, label),
                h: label.length * lineHeight
            };
        }

        return {
            w: ctx.measureText(label).width,
            h: lineHeight
        };
    }

    function determineLimits(angle, pos, size, min, max) {
        if (angle === min || angle === max) {
            return {
                start: pos - (size / 2),
                end: pos + (size / 2)
            };
        } else if (angle < min || angle > max) {
            return {
                start: pos - size,
                end: pos
            };
        }

        return {
            start: pos,
            end: pos + size
        };
    }

    /**
     * Helper function to fit a radial linear scale with point labels
     */
    function fitWithPointLabels(scale) {

        // Right, this is really confusing and there is a lot of maths going on here
        // The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
        //
        // Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
        //
        // Solution:
        //
        // We assume the radius of the polygon is half the size of the canvas at first
        // at each index we check if the text overlaps.
        //
        // Where it does, we store that angle and that index.
        //
        // After finding the largest index and angle we calculate how much we need to remove
        // from the shape radius to move the point inwards by that x.
        //
        // We average the left and right distances to get the maximum shape radius that can fit in the box
        // along with labels.
        //
        // Once we have that, we can find the centre point for the chart, by taking the x text protrusion
        // on each side, removing that from the size, halving it and adding the left x protrusion width.
        //
        // This will mean we have a shape fitted to the canvas, as large as it can be with the labels
        // and position it in the most space efficient manner
        //
        // https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif

        var plFont = helpers$1.options._parseFont(scale.options.pointLabels);

        // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
        // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
        var furthestLimits = {
            l: 0,
            r: scale.width,
            t: 0,
            b: scale.height - scale.paddingTop
        };
        var furthestAngles = {};
        var i, textSize, pointPosition;

        scale.ctx.font = plFont.string;
        scale._pointLabelSizes = [];

        var valueCount = getValueCount(scale);
        for (i = 0; i < valueCount; i++) {
            pointPosition = scale.getPointPosition(i, scale.drawingArea + 5);
            textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i] || '');
            scale._pointLabelSizes[i] = textSize;

            // Add quarter circle to make degree 0 mean top of circle
            var angleRadians = scale.getIndexAngle(i);
            var angle = helpers$1.toDegrees(angleRadians) % 360;
            var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
            var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);

            if (hLimits.start < furthestLimits.l) {
                furthestLimits.l = hLimits.start;
                furthestAngles.l = angleRadians;
            }

            if (hLimits.end > furthestLimits.r) {
                furthestLimits.r = hLimits.end;
                furthestAngles.r = angleRadians;
            }

            if (vLimits.start < furthestLimits.t) {
                furthestLimits.t = vLimits.start;
                furthestAngles.t = angleRadians;
            }

            if (vLimits.end > furthestLimits.b) {
                furthestLimits.b = vLimits.end;
                furthestAngles.b = angleRadians;
            }
        }

        scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles);
    }

    function getTextAlignForAngle(angle) {
        if (angle === 0 || angle === 180) {
            return 'center';
        } else if (angle < 180) {
            return 'left';
        }

        return 'right';
    }

    function fillText(ctx, text, position, lineHeight) {
        var y = position.y + lineHeight / 2;
        var i, ilen;

        if (helpers$1.isArray(text)) {
            for (i = 0, ilen = text.length; i < ilen; ++i) {
                ctx.fillText(text[i], position.x, y);
                y += lineHeight;
            }
        } else {
            ctx.fillText(text, position.x, y);
        }
    }

    function adjustPointPositionForLabelHeight(angle, textSize, position) {
        if (angle === 90 || angle === 270) {
            position.y -= (textSize.h / 2);
        } else if (angle > 270 || angle < 90) {
            position.y -= textSize.h;
        }
    }

    function drawPointLabels(scale) {
        var ctx = scale.ctx;
        var opts = scale.options;
        var angleLineOpts = opts.angleLines;
        var gridLineOpts = opts.gridLines;
        var pointLabelOpts = opts.pointLabels;
        var lineWidth = valueOrDefault$b(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
        var lineColor = valueOrDefault$b(angleLineOpts.color, gridLineOpts.color);
        var tickBackdropHeight = getTickBackdropHeight(opts);

        ctx.save();
        ctx.lineWidth = lineWidth;
        ctx.strokeStyle = lineColor;
        if (ctx.setLineDash) {
            ctx.setLineDash(resolve$7([angleLineOpts.borderDash, gridLineOpts.borderDash, []]));
            ctx.lineDashOffset = resolve$7([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]);
        }

        var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);

        // Point Label Font
        var plFont = helpers$1.options._parseFont(pointLabelOpts);

        ctx.font = plFont.string;
        ctx.textBaseline = 'middle';

        for (var i = getValueCount(scale) - 1; i >= 0; i--) {
            if (angleLineOpts.display && lineWidth && lineColor) {
                var outerPosition = scale.getPointPosition(i, outerDistance);
                ctx.beginPath();
                ctx.moveTo(scale.xCenter, scale.yCenter);
                ctx.lineTo(outerPosition.x, outerPosition.y);
                ctx.stroke();
            }

            if (pointLabelOpts.display) {
                // Extra pixels out for some label spacing
                var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
                var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);

                // Keep this in loop since we may support array properties here
                var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor);
                ctx.fillStyle = pointLabelFontColor;

                var angleRadians = scale.getIndexAngle(i);
                var angle = helpers$1.toDegrees(angleRadians);
                ctx.textAlign = getTextAlignForAngle(angle);
                adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
                fillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.lineHeight);
            }
        }
        ctx.restore();
    }

    function drawRadiusLine(scale, gridLineOpts, radius, index) {
        var ctx = scale.ctx;
        var circular = gridLineOpts.circular;
        var valueCount = getValueCount(scale);
        var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1);
        var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1);
        var pointPosition;

        if ((!circular && !valueCount) || !lineColor || !lineWidth) {
            return;
        }

        ctx.save();
        ctx.strokeStyle = lineColor;
        ctx.lineWidth = lineWidth;
        if (ctx.setLineDash) {
            ctx.setLineDash(gridLineOpts.borderDash || []);
            ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0;
        }

        ctx.beginPath();
        if (circular) {
            // Draw circular arcs between the points
            ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
        } else {
            // Draw straight lines connecting each index
            pointPosition = scale.getPointPosition(0, radius);
            ctx.moveTo(pointPosition.x, pointPosition.y);

            for (var i = 1; i < valueCount; i++) {
                pointPosition = scale.getPointPosition(i, radius);
                ctx.lineTo(pointPosition.x, pointPosition.y);
            }
        }
        ctx.closePath();
        ctx.stroke();
        ctx.restore();
    }

    function numberOrZero(param) {
        return helpers$1.isNumber(param) ? param : 0;
    }

    var scale_radialLinear = scale_linearbase.extend({
        setDimensions: function () {
            var me = this;

            // Set the unconstrained dimension before label rotation
            me.width = me.maxWidth;
            me.height = me.maxHeight;
            me.paddingTop = getTickBackdropHeight(me.options) / 2;
            me.xCenter = Math.floor(me.width / 2);
            me.yCenter = Math.floor((me.height - me.paddingTop) / 2);
            me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2;
        },

        determineDataLimits: function () {
            var me = this;
            var chart = me.chart;
            var min = Number.POSITIVE_INFINITY;
            var max = Number.NEGATIVE_INFINITY;

            helpers$1.each(chart.data.datasets, function (dataset, datasetIndex) {
                if (chart.isDatasetVisible(datasetIndex)) {
                    var meta = chart.getDatasetMeta(datasetIndex);

                    helpers$1.each(dataset.data, function (rawValue, index) {
                        var value = +me.getRightValue(rawValue);
                        if (isNaN(value) || meta.data[index].hidden) {
                            return;
                        }

                        min = Math.min(value, min);
                        max = Math.max(value, max);
                    });
                }
            });

            me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
            me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);

            // Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
            me.handleTickRangeOptions();
        },

        // Returns the maximum number of ticks based on the scale dimension
        _computeTickLimit: function () {
            return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
        },

        convertTicksToLabels: function () {
            var me = this;

            scale_linearbase.prototype.convertTicksToLabels.call(me);

            // Point labels
            me.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);
        },

        getLabelForIndex: function (index, datasetIndex) {
            return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
        },

        fit: function () {
            var me = this;
            var opts = me.options;

            if (opts.display && opts.pointLabels.display) {
                fitWithPointLabels(me);
            } else {
                me.setCenterPoint(0, 0, 0, 0);
            }
        },

        /**
         * Set radius reductions and determine new radius and center point
         * @private
         */
        setReductions: function (largestPossibleRadius, furthestLimits, furthestAngles) {
            var me = this;
            var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
            var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
            var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
            var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);

            radiusReductionLeft = numberOrZero(radiusReductionLeft);
            radiusReductionRight = numberOrZero(radiusReductionRight);
            radiusReductionTop = numberOrZero(radiusReductionTop);
            radiusReductionBottom = numberOrZero(radiusReductionBottom);

            me.drawingArea = Math.min(
                Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
                Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
            me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
        },

        setCenterPoint: function (leftMovement, rightMovement, topMovement, bottomMovement) {
            var me = this;
            var maxRight = me.width - rightMovement - me.drawingArea;
            var maxLeft = leftMovement + me.drawingArea;
            var maxTop = topMovement + me.drawingArea;
            var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;

            me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
            me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
        },

        getIndexAngle: function (index) {
            var angleMultiplier = (Math.PI * 2) / getValueCount(this);
            var startAngle = this.chart.options && this.chart.options.startAngle ?
                this.chart.options.startAngle :
                0;

            var startAngleRadians = startAngle * Math.PI * 2 / 360;

            // Start from the top instead of right, so remove a quarter of the circle
            return index * angleMultiplier + startAngleRadians;
        },

        getDistanceFromCenterForValue: function (value) {
            var me = this;

            if (value === null) {
                return 0; // null always in center
            }

            // Take into account half font size + the yPadding of the top value
            var scalingFactor = me.drawingArea / (me.max - me.min);
            if (me.options.ticks.reverse) {
                return (me.max - value) * scalingFactor;
            }
            return (value - me.min) * scalingFactor;
        },

        getPointPosition: function (index, distanceFromCenter) {
            var me = this;
            var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
            return {
                x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter,
                y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter
            };
        },

        getPointPositionForValue: function (index, value) {
            return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
        },

        getBasePosition: function () {
            var me = this;
            var min = me.min;
            var max = me.max;

            return me.getPointPositionForValue(0,
                me.beginAtZero ? 0 :
                    min < 0 && max < 0 ? max :
                        min > 0 && max > 0 ? min :
                            0);
        },

        draw: function () {
            var me = this;
            var opts = me.options;
            var gridLineOpts = opts.gridLines;
            var tickOpts = opts.ticks;

            if (opts.display) {
                var ctx = me.ctx;
                var startAngle = this.getIndexAngle(0);
                var tickFont = helpers$1.options._parseFont(tickOpts);

                if (opts.angleLines.display || opts.pointLabels.display) {
                    drawPointLabels(me);
                }

                helpers$1.each(me.ticks, function (label, index) {
                    // Don't draw a centre value (if it is minimum)
                    if (index > 0 || tickOpts.reverse) {
                        var yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);

                        // Draw circular lines around the scale
                        if (gridLineOpts.display && index !== 0) {
                            drawRadiusLine(me, gridLineOpts, yCenterOffset, index);
                        }

                        if (tickOpts.display) {
                            var tickFontColor = valueOrDefault$b(tickOpts.fontColor, core_defaults.global.defaultFontColor);
                            ctx.font = tickFont.string;

                            ctx.save();
                            ctx.translate(me.xCenter, me.yCenter);
                            ctx.rotate(startAngle);

                            if (tickOpts.showLabelBackdrop) {
                                var labelWidth = ctx.measureText(label).width;
                                ctx.fillStyle = tickOpts.backdropColor;
                                ctx.fillRect(
                                    -labelWidth / 2 - tickOpts.backdropPaddingX,
                                    -yCenterOffset - tickFont.size / 2 - tickOpts.backdropPaddingY,
                                    labelWidth + tickOpts.backdropPaddingX * 2,
                                    tickFont.size + tickOpts.backdropPaddingY * 2
                                );
                            }

                            ctx.textAlign = 'center';
                            ctx.textBaseline = 'middle';
                            ctx.fillStyle = tickFontColor;
                            ctx.fillText(label, 0, -yCenterOffset);
                            ctx.restore();
                        }
                    }
                });
            }
        }
    });

    // INTERNAL: static default options, registered in src/index.js
    var _defaults$3 = defaultConfig$3;
    scale_radialLinear._defaults = _defaults$3;

    var valueOrDefault$c = helpers$1.valueOrDefault;

    // Integer constants are from the ES6 spec.
    var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
    var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;

    var INTERVALS = {
        millisecond: {
            common: true,
            size: 1,
            steps: [1, 2, 5, 10, 20, 50, 100, 250, 500]
        },
        second: {
            common: true,
            size: 1000,
            steps: [1, 2, 5, 10, 15, 30]
        },
        minute: {
            common: true,
            size: 60000,
            steps: [1, 2, 5, 10, 15, 30]
        },
        hour: {
            common: true,
            size: 3600000,
            steps: [1, 2, 3, 6, 12]
        },
        day: {
            common: true,
            size: 86400000,
            steps: [1, 2, 5]
        },
        week: {
            common: false,
            size: 604800000,
            steps: [1, 2, 3, 4]
        },
        month: {
            common: true,
            size: 2.628e9,
            steps: [1, 2, 3]
        },
        quarter: {
            common: false,
            size: 7.884e9,
            steps: [1, 2, 3, 4]
        },
        year: {
            common: true,
            size: 3.154e10
        }
    };

    var UNITS = Object.keys(INTERVALS);

    function sorter(a, b) {
        return a - b;
    }

    function arrayUnique(items) {
        var hash = {};
        var out = [];
        var i, ilen, item;

        for (i = 0, ilen = items.length; i < ilen; ++i) {
            item = items[i];
            if (!hash[item]) {
                hash[item] = true;
                out.push(item);
            }
        }

        return out;
    }

    /**
     * Returns an array of {time, pos} objects used to interpolate a specific `time` or position
     * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
     * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
     * extremity (left + width or top + height). Note that it would be more optimized to directly
     * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
     * to create the lookup table. The table ALWAYS contains at least two items: min and max.
     *
     * @param {number[]} timestamps - timestamps sorted from lowest to highest.
     * @param {string} distribution - If 'linear', timestamps will be spread linearly along the min
     * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
     * If 'series', timestamps will be positioned at the same distance from each other. In this
     * case, only timestamps that break the time linearity are registered, meaning that in the
     * best case, all timestamps are linear, the table contains only min and max.
     */
    function buildLookupTable(timestamps, min, max, distribution) {
        if (distribution === 'linear' || !timestamps.length) {
            return [
                { time: min, pos: 0 },
                { time: max, pos: 1 }
            ];
        }

        var table = [];
        var items = [min];
        var i, ilen, prev, curr, next;

        for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
            curr = timestamps[i];
            if (curr > min && curr < max) {
                items.push(curr);
            }
        }

        items.push(max);

        for (i = 0, ilen = items.length; i < ilen; ++i) {
            next = items[i + 1];
            prev = items[i - 1];
            curr = items[i];

            // only add points that breaks the scale linearity
            if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
                table.push({ time: curr, pos: i / (ilen - 1) });
            }
        }

        return table;
    }

    // @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
    function lookup(table, key, value) {
        var lo = 0;
        var hi = table.length - 1;
        var mid, i0, i1;

        while (lo >= 0 && lo <= hi) {
            mid = (lo + hi) >> 1;
            i0 = table[mid - 1] || null;
            i1 = table[mid];

            if (!i0) {
                // given value is outside table (before first item)
                return { lo: null, hi: i1 };
            } else if (i1[key] < value) {
                lo = mid + 1;
            } else if (i0[key] > value) {
                hi = mid - 1;
            } else {
                return { lo: i0, hi: i1 };
            }
        }

        // given value is outside table (after last item)
        return { lo: i1, hi: null };
    }

    /**
     * Linearly interpolates the given source `value` using the table items `skey` values and
     * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
     * returns the position for a timestamp equal to 42. If value is out of bounds, values at
     * index [0, 1] or [n - 1, n] are used for the interpolation.
     */
    function interpolate$1(table, skey, sval, tkey) {
        var range = lookup(table, skey, sval);

        // Note: the lookup table ALWAYS contains at least 2 items (min and max)
        var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
        var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;

        var span = next[skey] - prev[skey];
        var ratio = span ? (sval - prev[skey]) / span : 0;
        var offset = (next[tkey] - prev[tkey]) * ratio;

        return prev[tkey] + offset;
    }

    function toTimestamp(scale, input) {
        var adapter = scale._adapter;
        var options = scale.options.time;
        var parser = options.parser;
        var format = parser || options.format;
        var value = input;

        if (typeof parser === 'function') {
            value = parser(value);
        }

        // Only parse if its not a timestamp already
        if (!helpers$1.isFinite(value)) {
            value = typeof format === 'string'
                ? adapter.parse(value, format)
                : adapter.parse(value);
        }

        if (value !== null) {
            return +value;
        }

        // Labels are in an incompatible format and no `parser` has been provided.
        // The user might still use the deprecated `format` option for parsing.
        if (!parser && typeof format === 'function') {
            value = format(input);

            // `format` could return something else than a timestamp, if so, parse it
            if (!helpers$1.isFinite(value)) {
                value = adapter.parse(value);
            }
        }

        return value;
    }

    function parse(scale, input) {
        if (helpers$1.isNullOrUndef(input)) {
            return null;
        }

        var options = scale.options.time;
        var value = toTimestamp(scale, scale.getRightValue(input));
        if (value === null) {
            return value;
        }

        if (options.round) {
            value = +scale._adapter.startOf(value, options.round);
        }

        return value;
    }

    /**
     * Returns the number of unit to skip to be able to display up to `capacity` number of ticks
     * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.
     */
    function determineStepSize(min, max, unit, capacity) {
        var range = max - min;
        var interval = INTERVALS[unit];
        var milliseconds = interval.size;
        var steps = interval.steps;
        var i, ilen, factor;

        if (!steps) {
            return Math.ceil(range / (capacity * milliseconds));
        }

        for (i = 0, ilen = steps.length; i < ilen; ++i) {
            factor = steps[i];
            if (Math.ceil(range / (milliseconds * factor)) <= capacity) {
                break;
            }
        }

        return factor;
    }

    /**
     * Figures out what unit results in an appropriate number of auto-generated ticks
     */
    function determineUnitForAutoTicks(minUnit, min, max, capacity) {
        var ilen = UNITS.length;
        var i, interval, factor;

        for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
            interval = INTERVALS[UNITS[i]];
            factor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;

            if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
                return UNITS[i];
            }
        }

        return UNITS[ilen - 1];
    }

    /**
     * Figures out what unit to format a set of ticks with
     */
    function determineUnitForFormatting(scale, ticks, minUnit, min, max) {
        var ilen = UNITS.length;
        var i, unit;

        for (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) {
            unit = UNITS[i];
            if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= ticks.length) {
                return unit;
            }
        }

        return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
    }

    function determineMajorUnit(unit) {
        for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
            if (INTERVALS[UNITS[i]].common) {
                return UNITS[i];
            }
        }
    }

    /**
     * Generates a maximum of `capacity` timestamps between min and max, rounded to the
     * `minor` unit, aligned on the `major` unit and using the given scale time `options`.
     * Important: this method can return ticks outside the min and max range, it's the
     * responsibility of the calling code to clamp values if needed.
     */
    function generate(scale, min, max, capacity) {
        var adapter = scale._adapter;
        var options = scale.options;
        var timeOpts = options.time;
        var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
        var major = determineMajorUnit(minor);
        var stepSize = valueOrDefault$c(timeOpts.stepSize, timeOpts.unitStepSize);
        var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
        var majorTicksEnabled = options.ticks.major.enabled;
        var interval = INTERVALS[minor];
        var first = min;
        var last = max;
        var ticks = [];
        var time;

        if (!stepSize) {
            stepSize = determineStepSize(min, max, minor, capacity);
        }

        // For 'week' unit, handle the first day of week option
        if (weekday) {
            first = +adapter.startOf(first, 'isoWeek', weekday);
            last = +adapter.startOf(last, 'isoWeek', weekday);
        }

        // Align first/last ticks on unit
        first = +adapter.startOf(first, weekday ? 'day' : minor);
        last = +adapter.startOf(last, weekday ? 'day' : minor);

        // Make sure that the last tick include max
        if (last < max) {
            last = +adapter.add(last, 1, minor);
        }

        time = first;

        if (majorTicksEnabled && major && !weekday && !timeOpts.round) {
            // Align the first tick on the previous `minor` unit aligned on the `major` unit:
            // we first aligned time on the previous `major` unit then add the number of full
            // stepSize there is between first and the previous major time.
            time = +adapter.startOf(time, major);
            time = +adapter.add(time, ~~((first - time) / (interval.size * stepSize)) * stepSize, minor);
        }

        for (; time < last; time = +adapter.add(time, stepSize, minor)) {
            ticks.push(+time);
        }

        ticks.push(+time);

        return ticks;
    }

    /**
     * Returns the start and end offsets from edges in the form of {start, end}
     * where each value is a relative width to the scale and ranges between 0 and 1.
     * They add extra margins on the both sides by scaling down the original scale.
     * Offsets are added when the `offset` option is true.
     */
    function computeOffsets(table, ticks, min, max, options) {
        var start = 0;
        var end = 0;
        var first, last;

        if (options.offset && ticks.length) {
            if (!options.time.min) {
                first = interpolate$1(table, 'time', ticks[0], 'pos');
                if (ticks.length === 1) {
                    start = 1 - first;
                } else {
                    start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2;
                }
            }
            if (!options.time.max) {
                last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos');
                if (ticks.length === 1) {
                    end = last;
                } else {
                    end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2;
                }
            }
        }

        return { start: start, end: end };
    }

    function ticksFromTimestamps(scale, values, majorUnit) {
        var ticks = [];
        var i, ilen, value, major;

        for (i = 0, ilen = values.length; i < ilen; ++i) {
            value = values[i];
            major = majorUnit ? value === +scale._adapter.startOf(value, majorUnit) : false;

            ticks.push({
                value: value,
                major: major
            });
        }

        return ticks;
    }

    var defaultConfig$4 = {
        position: 'bottom',

        /**
         * Data distribution along the scale:
         * - 'linear': data are spread according to their time (distances can vary),
         * - 'series': data are spread at the same distance from each other.
         * @see https://github.com/chartjs/Chart.js/pull/4507
         * @since 2.7.0
         */
        distribution: 'linear',

        /**
         * Scale boundary strategy (bypassed by min/max time options)
         * - `data`: make sure data are fully visible, ticks outside are removed
         * - `ticks`: make sure ticks are fully visible, data outside are truncated
         * @see https://github.com/chartjs/Chart.js/pull/4556
         * @since 2.7.0
         */
        bounds: 'data',

        adapters: {},
        time: {
            parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
            format: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from https://momentjs.com/docs/#/parsing/string-format/
            unit: false, // false == automatic or override with week, month, year, etc.
            round: false, // none, or override with week, month, year, etc.
            displayFormat: false, // DEPRECATED
            isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/
            minUnit: 'millisecond',
            displayFormats: {}
        },
        ticks: {
            autoSkip: false,

            /**
             * Ticks generation input values:
             * - 'auto': generates "optimal" ticks based on scale size and time options.
             * - 'data': generates ticks from data (including labels from data {t|x|y} objects).
             * - 'labels': generates ticks from user given `data.labels` values ONLY.
             * @see https://github.com/chartjs/Chart.js/pull/4507
             * @since 2.7.0
             */
            source: 'auto',

            major: {
                enabled: false
            }
        }
    };

    var scale_time = core_scale.extend({
        initialize: function () {
            this.mergeTicksOptions();
            core_scale.prototype.initialize.call(this);
        },

        update: function () {
            var me = this;
            var options = me.options;
            var time = options.time || (options.time = {});
            var adapter = me._adapter = new core_adapters._date(options.adapters.date);

            // DEPRECATIONS: output a message only one time per update
            if (time.format) {
                console.warn('options.time.format is deprecated and replaced by options.time.parser.');
            }

            // Backward compatibility: before introducing adapter, `displayFormats` was
            // supposed to contain *all* unit/string pairs but this can't be resolved
            // when loading the scale (adapters are loaded afterward), so let's populate
            // missing formats on update
            helpers$1.mergeIf(time.displayFormats, adapter.formats());

            return core_scale.prototype.update.apply(me, arguments);
        },

        /**
         * Allows data to be referenced via 't' attribute
         */
        getRightValue: function (rawValue) {
            if (rawValue && rawValue.t !== undefined) {
                rawValue = rawValue.t;
            }
            return core_scale.prototype.getRightValue.call(this, rawValue);
        },

        determineDataLimits: function () {
            var me = this;
            var chart = me.chart;
            var adapter = me._adapter;
            var timeOpts = me.options.time;
            var unit = timeOpts.unit || 'day';
            var min = MAX_INTEGER;
            var max = MIN_INTEGER;
            var timestamps = [];
            var datasets = [];
            var labels = [];
            var i, j, ilen, jlen, data, timestamp;
            var dataLabels = chart.data.labels || [];

            // Convert labels to timestamps
            for (i = 0, ilen = dataLabels.length; i < ilen; ++i) {
                labels.push(parse(me, dataLabels[i]));
            }

            // Convert data to timestamps
            for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
                if (chart.isDatasetVisible(i)) {
                    data = chart.data.datasets[i].data;

                    // Let's consider that all data have the same format.
                    if (helpers$1.isObject(data[0])) {
                        datasets[i] = [];

                        for (j = 0, jlen = data.length; j < jlen; ++j) {
                            timestamp = parse(me, data[j]);
                            timestamps.push(timestamp);
                            datasets[i][j] = timestamp;
                        }
                    } else {
                        for (j = 0, jlen = labels.length; j < jlen; ++j) {
                            timestamps.push(labels[j]);
                        }
                        datasets[i] = labels.slice(0);
                    }
                } else {
                    datasets[i] = [];
                }
            }

            if (labels.length) {
                // Sort labels **after** data have been converted
                labels = arrayUnique(labels).sort(sorter);
                min = Math.min(min, labels[0]);
                max = Math.max(max, labels[labels.length - 1]);
            }

            if (timestamps.length) {
                timestamps = arrayUnique(timestamps).sort(sorter);
                min = Math.min(min, timestamps[0]);
                max = Math.max(max, timestamps[timestamps.length - 1]);
            }

            min = parse(me, timeOpts.min) || min;
            max = parse(me, timeOpts.max) || max;

            // In case there is no valid min/max, set limits based on unit time option
            min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min;
            max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max;

            // Make sure that max is strictly higher than min (required by the lookup table)
            me.min = Math.min(min, max);
            me.max = Math.max(min + 1, max);

            // PRIVATE
            me._horizontal = me.isHorizontal();
            me._table = [];
            me._timestamps = {
                data: timestamps,
                datasets: datasets,
                labels: labels
            };
        },

        buildTicks: function () {
            var me = this;
            var min = me.min;
            var max = me.max;
            var options = me.options;
            var timeOpts = options.time;
            var timestamps = [];
            var ticks = [];
            var i, ilen, timestamp;

            switch (options.ticks.source) {
                case 'data':
                    timestamps = me._timestamps.data;
                    break;
                case 'labels':
                    timestamps = me._timestamps.labels;
                    break;
                case 'auto':
                default:
                    timestamps = generate(me, min, max, me.getLabelCapacity(min), options);
            }

            if (options.bounds === 'ticks' && timestamps.length) {
                min = timestamps[0];
                max = timestamps[timestamps.length - 1];
            }

            // Enforce limits with user min/max options
            min = parse(me, timeOpts.min) || min;
            max = parse(me, timeOpts.max) || max;

            // Remove ticks outside the min/max range
            for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
                timestamp = timestamps[i];
                if (timestamp >= min && timestamp <= max) {
                    ticks.push(timestamp);
                }
            }

            me.min = min;
            me.max = max;

            // PRIVATE
            me._unit = timeOpts.unit || determineUnitForFormatting(me, ticks, timeOpts.minUnit, me.min, me.max);
            me._majorUnit = determineMajorUnit(me._unit);
            me._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);
            me._offsets = computeOffsets(me._table, ticks, min, max, options);

            if (options.ticks.reverse) {
                ticks.reverse();
            }

            return ticksFromTimestamps(me, ticks, me._majorUnit);
        },

        getLabelForIndex: function (index, datasetIndex) {
            var me = this;
            var adapter = me._adapter;
            var data = me.chart.data;
            var timeOpts = me.options.time;
            var label = data.labels && index < data.labels.length ? data.labels[index] : '';
            var value = data.datasets[datasetIndex].data[index];

            if (helpers$1.isObject(value)) {
                label = me.getRightValue(value);
            }
            if (timeOpts.tooltipFormat) {
                return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat);
            }
            if (typeof label === 'string') {
                return label;
            }
            return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime);
        },

        /**
         * Function to format an individual tick mark
         * @private
         */
        tickFormatFunction: function (time, index, ticks, format) {
            var me = this;
            var adapter = me._adapter;
            var options = me.options;
            var formats = options.time.displayFormats;
            var minorFormat = formats[me._unit];
            var majorUnit = me._majorUnit;
            var majorFormat = formats[majorUnit];
            var majorTime = +adapter.startOf(time, majorUnit);
            var majorTickOpts = options.ticks.major;
            var major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;
            var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat);
            var tickOpts = major ? majorTickOpts : options.ticks.minor;
            var formatter = valueOrDefault$c(tickOpts.callback, tickOpts.userCallback);

            return formatter ? formatter(label, index, ticks) : label;
        },

        convertTicksToLabels: function (ticks) {
            var labels = [];
            var i, ilen;

            for (i = 0, ilen = ticks.length; i < ilen; ++i) {
                labels.push(this.tickFormatFunction(ticks[i].value, i, ticks));
            }

            return labels;
        },

        /**
         * @private
         */
        getPixelForOffset: function (time) {
            var me = this;
            var isReverse = me.options.ticks.reverse;
            var size = me._horizontal ? me.width : me.height;
            var start = me._horizontal ? isReverse ? me.right : me.left : isReverse ? me.bottom : me.top;
            var pos = interpolate$1(me._table, 'time', time, 'pos');
            var offset = size * (me._offsets.start + pos) / (me._offsets.start + 1 + me._offsets.end);

            return isReverse ? start - offset : start + offset;
        },

        getPixelForValue: function (value, index, datasetIndex) {
            var me = this;
            var time = null;

            if (index !== undefined && datasetIndex !== undefined) {
                time = me._timestamps.datasets[datasetIndex][index];
            }

            if (time === null) {
                time = parse(me, value);
            }

            if (time !== null) {
                return me.getPixelForOffset(time);
            }
        },

        getPixelForTick: function (index) {
            var ticks = this.getTicks();
            return index >= 0 && index < ticks.length ?
                this.getPixelForOffset(ticks[index].value) :
                null;
        },

        getValueForPixel: function (pixel) {
            var me = this;
            var size = me._horizontal ? me.width : me.height;
            var start = me._horizontal ? me.left : me.top;
            var pos = (size ? (pixel - start) / size : 0) * (me._offsets.start + 1 + me._offsets.start) - me._offsets.end;
            var time = interpolate$1(me._table, 'pos', pos, 'time');

            // DEPRECATION, we should return time directly
            return me._adapter._create(time);
        },

        /**
         * Crude approximation of what the label width might be
         * @private
         */
        getLabelWidth: function (label) {
            var me = this;
            var ticksOpts = me.options.ticks;
            var tickLabelWidth = me.ctx.measureText(label).width;
            var angle = helpers$1.toRadians(ticksOpts.maxRotation);
            var cosRotation = Math.cos(angle);
            var sinRotation = Math.sin(angle);
            var tickFontSize = valueOrDefault$c(ticksOpts.fontSize, core_defaults.global.defaultFontSize);

            return (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);
        },

        /**
         * @private
         */
        getLabelCapacity: function (exampleTime) {
            var me = this;

            // pick the longest format (milliseconds) for guestimation
            var format = me.options.time.displayFormats.millisecond;
            var exampleLabel = me.tickFormatFunction(exampleTime, 0, [], format);
            var tickLabelWidth = me.getLabelWidth(exampleLabel);
            var innerWidth = me.isHorizontal() ? me.width : me.height;
            var capacity = Math.floor(innerWidth / tickLabelWidth);

            return capacity > 0 ? capacity : 1;
        }
    });

    // INTERNAL: static default options, registered in src/index.js
    var _defaults$4 = defaultConfig$4;
    scale_time._defaults = _defaults$4;

    var scales = {
        category: scale_category,
        linear: scale_linear,
        logarithmic: scale_logarithmic,
        radialLinear: scale_radialLinear,
        time: scale_time
    };

    var FORMATS = {
        datetime: 'MMM D, YYYY, h:mm:ss a',
        millisecond: 'h:mm:ss.SSS a',
        second: 'h:mm:ss a',
        minute: 'h:mm a',
        hour: 'hA',
        day: 'MMM D',
        week: 'll',
        month: 'MMM YYYY',
        quarter: '[Q]Q - YYYY',
        year: 'YYYY'
    };

    core_adapters._date.override(typeof moment === 'function' ? {
        _id: 'moment', // DEBUG ONLY

        formats: function () {
            return FORMATS;
        },

        parse: function (value, format) {
            if (typeof value === 'string' && typeof format === 'string') {
                value = moment(value, format);
            } else if (!(value instanceof moment)) {
                value = moment(value);
            }
            return value.isValid() ? value.valueOf() : null;
        },

        format: function (time, format) {
            return moment(time).format(format);
        },

        add: function (time, amount, unit) {
            return moment(time).add(amount, unit).valueOf();
        },

        diff: function (max, min, unit) {
            return moment.duration(moment(max).diff(moment(min))).as(unit);
        },

        startOf: function (time, unit, weekday) {
            time = moment(time);
            if (unit === 'isoWeek') {
                return time.isoWeekday(weekday).valueOf();
            }
            return time.startOf(unit).valueOf();
        },

        endOf: function (time, unit) {
            return moment(time).endOf(unit).valueOf();
        },

        // DEPRECATIONS

        /**
         * Provided for backward compatibility with scale.getValueForPixel().
         * @deprecated since version 2.8.0
         * @todo remove at version 3
         * @private
         */
        _create: function (time) {
            return moment(time);
        },
    } : {});

    core_defaults._set('global', {
        plugins: {
            filler: {
                propagate: true
            }
        }
    });

    var mappers = {
        dataset: function (source) {
            var index = source.fill;
            var chart = source.chart;
            var meta = chart.getDatasetMeta(index);
            var visible = meta && chart.isDatasetVisible(index);
            var points = (visible && meta.dataset._children) || [];
            var length = points.length || 0;

            return !length ? null : function (point, i) {
                return (i < length && points[i]._view) || null;
            };
        },

        boundary: function (source) {
            var boundary = source.boundary;
            var x = boundary ? boundary.x : null;
            var y = boundary ? boundary.y : null;

            return function (point) {
                return {
                    x: x === null ? point.x : x,
                    y: y === null ? point.y : y,
                };
            };
        }
    };

    // @todo if (fill[0] === '#')
    function decodeFill(el, index, count) {
        var model = el._model || {};
        var fill = model.fill;
        var target;

        if (fill === undefined) {
            fill = !!model.backgroundColor;
        }

        if (fill === false || fill === null) {
            return false;
        }

        if (fill === true) {
            return 'origin';
        }

        target = parseFloat(fill, 10);
        if (isFinite(target) && Math.floor(target) === target) {
            if (fill[0] === '-' || fill[0] === '+') {
                target = index + target;
            }

            if (target === index || target < 0 || target >= count) {
                return false;
            }

            return target;
        }

        switch (fill) {
            // compatibility
            case 'bottom':
                return 'start';
            case 'top':
                return 'end';
            case 'zero':
                return 'origin';
            // supported boundaries
            case 'origin':
            case 'start':
            case 'end':
                return fill;
            // invalid fill values
            default:
                return false;
        }
    }

    function computeBoundary(source) {
        var model = source.el._model || {};
        var scale = source.el._scale || {};
        var fill = source.fill;
        var target = null;
        var horizontal;

        if (isFinite(fill)) {
            return null;
        }

        // Backward compatibility: until v3, we still need to support boundary values set on
        // the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
        // controllers might still use it (e.g. the Smith chart).

        if (fill === 'start') {
            target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
        } else if (fill === 'end') {
            target = model.scaleTop === undefined ? scale.top : model.scaleTop;
        } else if (model.scaleZero !== undefined) {
            target = model.scaleZero;
        } else if (scale.getBasePosition) {
            target = scale.getBasePosition();
        } else if (scale.getBasePixel) {
            target = scale.getBasePixel();
        }

        if (target !== undefined && target !== null) {
            if (target.x !== undefined && target.y !== undefined) {
                return target;
            }

            if (helpers$1.isFinite(target)) {
                horizontal = scale.isHorizontal();
                return {
                    x: horizontal ? target : null,
                    y: horizontal ? null : target
                };
            }
        }

        return null;
    }

    function resolveTarget(sources, index, propagate) {
        var source = sources[index];
        var fill = source.fill;
        var visited = [index];
        var target;

        if (!propagate) {
            return fill;
        }

        while (fill !== false && visited.indexOf(fill) === -1) {
            if (!isFinite(fill)) {
                return fill;
            }

            target = sources[fill];
            if (!target) {
                return false;
            }

            if (target.visible) {
                return fill;
            }

            visited.push(fill);
            fill = target.fill;
        }

        return false;
    }

    function createMapper(source) {
        var fill = source.fill;
        var type = 'dataset';

        if (fill === false) {
            return null;
        }

        if (!isFinite(fill)) {
            type = 'boundary';
        }

        return mappers[type](source);
    }

    function isDrawable(point) {
        return point && !point.skip;
    }

    function drawArea(ctx, curve0, curve1, len0, len1) {
        var i;

        if (!len0 || !len1) {
            return;
        }

        // building first area curve (normal)
        ctx.moveTo(curve0[0].x, curve0[0].y);
        for (i = 1; i < len0; ++i) {
            helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
        }

        // joining the two area curves
        ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);

        // building opposite area curve (reverse)
        for (i = len1 - 1; i > 0; --i) {
            helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
        }
    }

    function doFill(ctx, points, mapper, view, color, loop) {
        var count = points.length;
        var span = view.spanGaps;
        var curve0 = [];
        var curve1 = [];
        var len0 = 0;
        var len1 = 0;
        var i, ilen, index, p0, p1, d0, d1;

        ctx.beginPath();

        for (i = 0, ilen = (count + !!loop); i < ilen; ++i) {
            index = i % count;
            p0 = points[index]._view;
            p1 = mapper(p0, index, view);
            d0 = isDrawable(p0);
            d1 = isDrawable(p1);

            if (d0 && d1) {
                len0 = curve0.push(p0);
                len1 = curve1.push(p1);
            } else if (len0 && len1) {
                if (!span) {
                    drawArea(ctx, curve0, curve1, len0, len1);
                    len0 = len1 = 0;
                    curve0 = [];
                    curve1 = [];
                } else {
                    if (d0) {
                        curve0.push(p0);
                    }
                    if (d1) {
                        curve1.push(p1);
                    }
                }
            }
        }

        drawArea(ctx, curve0, curve1, len0, len1);

        ctx.closePath();
        ctx.fillStyle = color;
        ctx.fill();
    }

    var plugin_filler = {
        id: 'filler',

        afterDatasetsUpdate: function (chart, options) {
            var count = (chart.data.datasets || []).length;
            var propagate = options.propagate;
            var sources = [];
            var meta, i, el, source;

            for (i = 0; i < count; ++i) {
                meta = chart.getDatasetMeta(i);
                el = meta.dataset;
                source = null;

                if (el && el._model && el instanceof elements.Line) {
                    source = {
                        visible: chart.isDatasetVisible(i),
                        fill: decodeFill(el, i, count),
                        chart: chart,
                        el: el
                    };
                }

                meta.$filler = source;
                sources.push(source);
            }

            for (i = 0; i < count; ++i) {
                source = sources[i];
                if (!source) {
                    continue;
                }

                source.fill = resolveTarget(sources, i, propagate);
                source.boundary = computeBoundary(source);
                source.mapper = createMapper(source);
            }
        },

        beforeDatasetDraw: function (chart, args) {
            var meta = args.meta.$filler;
            if (!meta) {
                return;
            }

            var ctx = chart.ctx;
            var el = meta.el;
            var view = el._view;
            var points = el._children || [];
            var mapper = meta.mapper;
            var color = view.backgroundColor || core_defaults.global.defaultColor;

            if (mapper && color && points.length) {
                helpers$1.canvas.clipArea(ctx, chart.chartArea);
                doFill(ctx, points, mapper, view, color, el._loop);
                helpers$1.canvas.unclipArea(ctx);
            }
        }
    };

    var noop$1 = helpers$1.noop;
    var valueOrDefault$d = helpers$1.valueOrDefault;

    core_defaults._set('global', {
        legend: {
            display: true,
            position: 'top',
            fullWidth: true,
            reverse: false,
            weight: 1000,

            // a callback that will handle
            onClick: function (e, legendItem) {
                var index = legendItem.datasetIndex;
                var ci = this.chart;
                var meta = ci.getDatasetMeta(index);

                // See controller.isDatasetVisible comment
                meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;

                // We hid a dataset ... rerender the chart
                ci.update();
            },

            onHover: null,
            onLeave: null,

            labels: {
                boxWidth: 40,
                padding: 10,
                // Generates labels shown in the legend
                // Valid properties to return:
                // text : text to display
                // fillStyle : fill of coloured box
                // strokeStyle: stroke of coloured box
                // hidden : if this legend item refers to a hidden item
                // lineCap : cap style for line
                // lineDash
                // lineDashOffset :
                // lineJoin :
                // lineWidth :
                generateLabels: function (chart) {
                    var data = chart.data;
                    return helpers$1.isArray(data.datasets) ? data.datasets.map(function (dataset, i) {
                        return {
                            text: dataset.label,
                            fillStyle: (!helpers$1.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),
                            hidden: !chart.isDatasetVisible(i),
                            lineCap: dataset.borderCapStyle,
                            lineDash: dataset.borderDash,
                            lineDashOffset: dataset.borderDashOffset,
                            lineJoin: dataset.borderJoinStyle,
                            lineWidth: dataset.borderWidth,
                            strokeStyle: dataset.borderColor,
                            pointStyle: dataset.pointStyle,

                            // Below is extra data used for toggling the datasets
                            datasetIndex: i
                        };
                    }, this) : [];
                }
            }
        },

        legendCallback: function (chart) {
            var text = [];
            text.push('<ul class="' + chart.id + '-legend">');
            for (var i = 0; i < chart.data.datasets.length; i++) {
                text.push('<li><span style="background-color:' + chart.data.datasets[i].backgroundColor + '"></span>');
                if (chart.data.datasets[i].label) {
                    text.push(chart.data.datasets[i].label);
                }
                text.push('</li>');
            }
            text.push('</ul>');
            return text.join('');
        }
    });

    /**
     * Helper function to get the box width based on the usePointStyle option
     * @param {object} labelopts - the label options on the legend
     * @param {number} fontSize - the label font size
     * @return {number} width of the color box area
     */
    function getBoxWidth(labelOpts, fontSize) {
        return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ?
            fontSize :
            labelOpts.boxWidth;
    }

    /**
     * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
     */
    var Legend = core_element.extend({

        initialize: function (config) {
            helpers$1.extend(this, config);

            // Contains hit boxes for each dataset (in dataset order)
            this.legendHitBoxes = [];

            /**
                 * @private
                 */
            this._hoveredItem = null;

            // Are we in doughnut mode which has a different data type
            this.doughnutMode = false;
        },

        // These methods are ordered by lifecycle. Utilities then follow.
        // Any function defined here is inherited by all legend types.
        // Any function can be extended by the legend type

        beforeUpdate: noop$1,
        update: function (maxWidth, maxHeight, margins) {
            var me = this;

            // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
            me.beforeUpdate();

            // Absorb the master measurements
            me.maxWidth = maxWidth;
            me.maxHeight = maxHeight;
            me.margins = margins;

            // Dimensions
            me.beforeSetDimensions();
            me.setDimensions();
            me.afterSetDimensions();
            // Labels
            me.beforeBuildLabels();
            me.buildLabels();
            me.afterBuildLabels();

            // Fit
            me.beforeFit();
            me.fit();
            me.afterFit();
            //
            me.afterUpdate();

            return me.minSize;
        },
        afterUpdate: noop$1,

        //

        beforeSetDimensions: noop$1,
        setDimensions: function () {
            var me = this;
            // Set the unconstrained dimension before label rotation
            if (me.isHorizontal()) {
                // Reset position before calculating rotation
                me.width = me.maxWidth;
                me.left = 0;
                me.right = me.width;
            } else {
                me.height = me.maxHeight;

                // Reset position before calculating rotation
                me.top = 0;
                me.bottom = me.height;
            }

            // Reset padding
            me.paddingLeft = 0;
            me.paddingTop = 0;
            me.paddingRight = 0;
            me.paddingBottom = 0;

            // Reset minSize
            me.minSize = {
                width: 0,
                height: 0
            };
        },
        afterSetDimensions: noop$1,

        //

        beforeBuildLabels: noop$1,
        buildLabels: function () {
            var me = this;
            var labelOpts = me.options.labels || {};
            var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || [];

            if (labelOpts.filter) {
                legendItems = legendItems.filter(function (item) {
                    return labelOpts.filter(item, me.chart.data);
                });
            }

            if (me.options.reverse) {
                legendItems.reverse();
            }

            me.legendItems = legendItems;
        },
        afterBuildLabels: noop$1,

        //

        beforeFit: noop$1,
        fit: function () {
            var me = this;
            var opts = me.options;
            var labelOpts = opts.labels;
            var display = opts.display;

            var ctx = me.ctx;

            var labelFont = helpers$1.options._parseFont(labelOpts);
            var fontSize = labelFont.size;

            // Reset hit boxes
            var hitboxes = me.legendHitBoxes = [];

            var minSize = me.minSize;
            var isHorizontal = me.isHorizontal();

            if (isHorizontal) {
                minSize.width = me.maxWidth; // fill all the width
                minSize.height = display ? 10 : 0;
            } else {
                minSize.width = display ? 10 : 0;
                minSize.height = me.maxHeight; // fill all the height
            }

            // Increase sizes here
            if (display) {
                ctx.font = labelFont.string;

                if (isHorizontal) {
                    // Labels

                    // Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
                    var lineWidths = me.lineWidths = [0];
                    var totalHeight = 0;

                    ctx.textAlign = 'left';
                    ctx.textBaseline = 'top';

                    helpers$1.each(me.legendItems, function (legendItem, i) {
                        var boxWidth = getBoxWidth(labelOpts, fontSize);
                        var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

                        if (i === 0 || lineWidths[lineWidths.length - 1] + width + labelOpts.padding > minSize.width) {
                            totalHeight += fontSize + labelOpts.padding;
                            lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = labelOpts.padding;
                        }

                        // Store the hitbox width and height here. Final position will be updated in `draw`
                        hitboxes[i] = {
                            left: 0,
                            top: 0,
                            width: width,
                            height: fontSize
                        };

                        lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
                    });

                    minSize.height += totalHeight;

                } else {
                    var vPadding = labelOpts.padding;
                    var columnWidths = me.columnWidths = [];
                    var totalWidth = labelOpts.padding;
                    var currentColWidth = 0;
                    var currentColHeight = 0;
                    var itemHeight = fontSize + vPadding;

                    helpers$1.each(me.legendItems, function (legendItem, i) {
                        var boxWidth = getBoxWidth(labelOpts, fontSize);
                        var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;

                        // If too tall, go to new column
                        if (i > 0 && currentColHeight + itemHeight > minSize.height - vPadding) {
                            totalWidth += currentColWidth + labelOpts.padding;
                            columnWidths.push(currentColWidth); // previous column width

                            currentColWidth = 0;
                            currentColHeight = 0;
                        }

                        // Get max width
                        currentColWidth = Math.max(currentColWidth, itemWidth);
                        currentColHeight += itemHeight;

                        // Store the hitbox width and height here. Final position will be updated in `draw`
                        hitboxes[i] = {
                            left: 0,
                            top: 0,
                            width: itemWidth,
                            height: fontSize
                        };
                    });

                    totalWidth += currentColWidth;
                    columnWidths.push(currentColWidth);
                    minSize.width += totalWidth;
                }
            }

            me.width = minSize.width;
            me.height = minSize.height;
        },
        afterFit: noop$1,

        // Shared Methods
        isHorizontal: function () {
            return this.options.position === 'top' || this.options.position === 'bottom';
        },

        // Actually draw the legend on the canvas
        draw: function () {
            var me = this;
            var opts = me.options;
            var labelOpts = opts.labels;
            var globalDefaults = core_defaults.global;
            var defaultColor = globalDefaults.defaultColor;
            var lineDefault = globalDefaults.elements.line;
            var legendWidth = me.width;
            var lineWidths = me.lineWidths;

            if (opts.display) {
                var ctx = me.ctx;
                var fontColor = valueOrDefault$d(labelOpts.fontColor, globalDefaults.defaultFontColor);
                var labelFont = helpers$1.options._parseFont(labelOpts);
                var fontSize = labelFont.size;
                var cursor;

                // Canvas setup
                ctx.textAlign = 'left';
                ctx.textBaseline = 'middle';
                ctx.lineWidth = 0.5;
                ctx.strokeStyle = fontColor; // for strikethrough effect
                ctx.fillStyle = fontColor; // render in correct colour
                ctx.font = labelFont.string;

                var boxWidth = getBoxWidth(labelOpts, fontSize);
                var hitboxes = me.legendHitBoxes;

                // current position
                var drawLegendBox = function (x, y, legendItem) {
                    if (isNaN(boxWidth) || boxWidth <= 0) {
                        return;
                    }

                    // Set the ctx for the box
                    ctx.save();

                    var lineWidth = valueOrDefault$d(legendItem.lineWidth, lineDefault.borderWidth);
                    ctx.fillStyle = valueOrDefault$d(legendItem.fillStyle, defaultColor);
                    ctx.lineCap = valueOrDefault$d(legendItem.lineCap, lineDefault.borderCapStyle);
                    ctx.lineDashOffset = valueOrDefault$d(legendItem.lineDashOffset, lineDefault.borderDashOffset);
                    ctx.lineJoin = valueOrDefault$d(legendItem.lineJoin, lineDefault.borderJoinStyle);
                    ctx.lineWidth = lineWidth;
                    ctx.strokeStyle = valueOrDefault$d(legendItem.strokeStyle, defaultColor);

                    if (ctx.setLineDash) {
                        // IE 9 and 10 do not support line dash
                        ctx.setLineDash(valueOrDefault$d(legendItem.lineDash, lineDefault.borderDash));
                    }

                    if (opts.labels && opts.labels.usePointStyle) {
                        // Recalculate x and y for drawPoint() because its expecting
                        // x and y to be center of figure (instead of top left)
                        var radius = boxWidth * Math.SQRT2 / 2;
                        var centerX = x + boxWidth / 2;
                        var centerY = y + fontSize / 2;

                        // Draw pointStyle as legend symbol
                        helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);
                    } else {
                        // Draw box as legend symbol
                        if (lineWidth !== 0) {
                            ctx.strokeRect(x, y, boxWidth, fontSize);
                        }
                        ctx.fillRect(x, y, boxWidth, fontSize);
                    }

                    ctx.restore();
                };
                var fillText = function (x, y, legendItem, textWidth) {
                    var halfFontSize = fontSize / 2;
                    var xLeft = boxWidth + halfFontSize + x;
                    var yMiddle = y + halfFontSize;

                    ctx.fillText(legendItem.text, xLeft, yMiddle);

                    if (legendItem.hidden) {
                        // Strikethrough the text if hidden
                        ctx.beginPath();
                        ctx.lineWidth = 2;
                        ctx.moveTo(xLeft, yMiddle);
                        ctx.lineTo(xLeft + textWidth, yMiddle);
                        ctx.stroke();
                    }
                };

                // Horizontal
                var isHorizontal = me.isHorizontal();
                if (isHorizontal) {
                    cursor = {
                        x: me.left + ((legendWidth - lineWidths[0]) / 2) + labelOpts.padding,
                        y: me.top + labelOpts.padding,
                        line: 0
                    };
                } else {
                    cursor = {
                        x: me.left + labelOpts.padding,
                        y: me.top + labelOpts.padding,
                        line: 0
                    };
                }

                var itemHeight = fontSize + labelOpts.padding;
                helpers$1.each(me.legendItems, function (legendItem, i) {
                    var textWidth = ctx.measureText(legendItem.text).width;
                    var width = boxWidth + (fontSize / 2) + textWidth;
                    var x = cursor.x;
                    var y = cursor.y;

                    // Use (me.left + me.minSize.width) and (me.top + me.minSize.height)
                    // instead of me.right and me.bottom because me.width and me.height
                    // may have been changed since me.minSize was calculated
                    if (isHorizontal) {
                        if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) {
                            y = cursor.y += itemHeight;
                            cursor.line++;
                            x = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2) + labelOpts.padding;
                        }
                    } else if (i > 0 && y + itemHeight > me.top + me.minSize.height) {
                        x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
                        y = cursor.y = me.top + labelOpts.padding;
                        cursor.line++;
                    }

                    drawLegendBox(x, y, legendItem);

                    hitboxes[i].left = x;
                    hitboxes[i].top = y;

                    // Fill the actual label
                    fillText(x, y, legendItem, textWidth);

                    if (isHorizontal) {
                        cursor.x += width + labelOpts.padding;
                    } else {
                        cursor.y += itemHeight;
                    }

                });
            }
        },

        /**
         * @private
         */
        _getLegendItemAt: function (x, y) {
            var me = this;
            var i, hitBox, lh;

            if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
                // See if we are touching one of the dataset boxes
                lh = me.legendHitBoxes;
                for (i = 0; i < lh.length; ++i) {
                    hitBox = lh[i];

                    if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
                        // Touching an element
                        return me.legendItems[i];
                    }
                }
            }

            return null;
        },

        /**
         * Handle an event
         * @private
         * @param {IEvent} event - The event to handle
         */
        handleEvent: function (e) {
            var me = this;
            var opts = me.options;
            var type = e.type === 'mouseup' ? 'click' : e.type;
            var hoveredItem;

            if (type === 'mousemove') {
                if (!opts.onHover && !opts.onLeave) {
                    return;
                }
            } else if (type === 'click') {
                if (!opts.onClick) {
                    return;
                }
            } else {
                return;
            }

            // Chart event already has relative position in it
            hoveredItem = me._getLegendItemAt(e.x, e.y);

            if (type === 'click') {
                if (hoveredItem && opts.onClick) {
                    // use e.native for backwards compatibility
                    opts.onClick.call(me, e.native, hoveredItem);
                }
            } else {
                if (opts.onLeave && hoveredItem !== me._hoveredItem) {
                    if (me._hoveredItem) {
                        opts.onLeave.call(me, e.native, me._hoveredItem);
                    }
                    me._hoveredItem = hoveredItem;
                }

                if (opts.onHover && hoveredItem) {
                    // use e.native for backwards compatibility
                    opts.onHover.call(me, e.native, hoveredItem);
                }
            }
        }
    });

    function createNewLegendAndAttach(chart, legendOpts) {
        var legend = new Legend({
            ctx: chart.ctx,
            options: legendOpts,
            chart: chart
        });

        core_layouts.configure(chart, legend, legendOpts);
        core_layouts.addBox(chart, legend);
        chart.legend = legend;
    }

    var plugin_legend = {
        id: 'legend',

        /**
         * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
         * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
         * the plugin, which one will be re-exposed in the chart.js file.
         * https://github.com/chartjs/Chart.js/pull/2640
         * @private
         */
        _element: Legend,

        beforeInit: function (chart) {
            var legendOpts = chart.options.legend;

            if (legendOpts) {
                createNewLegendAndAttach(chart, legendOpts);
            }
        },

        beforeUpdate: function (chart) {
            var legendOpts = chart.options.legend;
            var legend = chart.legend;

            if (legendOpts) {
                helpers$1.mergeIf(legendOpts, core_defaults.global.legend);

                if (legend) {
                    core_layouts.configure(chart, legend, legendOpts);
                    legend.options = legendOpts;
                } else {
                    createNewLegendAndAttach(chart, legendOpts);
                }
            } else if (legend) {
                core_layouts.removeBox(chart, legend);
                delete chart.legend;
            }
        },

        afterEvent: function (chart, e) {
            var legend = chart.legend;
            if (legend) {
                legend.handleEvent(e);
            }
        }
    };

    var noop$2 = helpers$1.noop;

    core_defaults._set('global', {
        title: {
            display: false,
            fontStyle: 'bold',
            fullWidth: true,
            padding: 10,
            position: 'top',
            text: '',
            weight: 2000         // by default greater than legend (1000) to be above
        }
    });

    /**
     * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
     */
    var Title = core_element.extend({
        initialize: function (config) {
            var me = this;
            helpers$1.extend(me, config);

            // Contains hit boxes for each dataset (in dataset order)
            me.legendHitBoxes = [];
        },

        // These methods are ordered by lifecycle. Utilities then follow.

        beforeUpdate: noop$2,
        update: function (maxWidth, maxHeight, margins) {
            var me = this;

            // Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
            me.beforeUpdate();

            // Absorb the master measurements
            me.maxWidth = maxWidth;
            me.maxHeight = maxHeight;
            me.margins = margins;

            // Dimensions
            me.beforeSetDimensions();
            me.setDimensions();
            me.afterSetDimensions();
            // Labels
            me.beforeBuildLabels();
            me.buildLabels();
            me.afterBuildLabels();

            // Fit
            me.beforeFit();
            me.fit();
            me.afterFit();
            //
            me.afterUpdate();

            return me.minSize;

        },
        afterUpdate: noop$2,

        //

        beforeSetDimensions: noop$2,
        setDimensions: function () {
            var me = this;
            // Set the unconstrained dimension before label rotation
            if (me.isHorizontal()) {
                // Reset position before calculating rotation
                me.width = me.maxWidth;
                me.left = 0;
                me.right = me.width;
            } else {
                me.height = me.maxHeight;

                // Reset position before calculating rotation
                me.top = 0;
                me.bottom = me.height;
            }

            // Reset padding
            me.paddingLeft = 0;
            me.paddingTop = 0;
            me.paddingRight = 0;
            me.paddingBottom = 0;

            // Reset minSize
            me.minSize = {
                width: 0,
                height: 0
            };
        },
        afterSetDimensions: noop$2,

        //

        beforeBuildLabels: noop$2,
        buildLabels: noop$2,
        afterBuildLabels: noop$2,

        //

        beforeFit: noop$2,
        fit: function () {
            var me = this;
            var opts = me.options;
            var display = opts.display;
            var minSize = me.minSize;
            var lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1;
            var fontOpts = helpers$1.options._parseFont(opts);
            var textSize = display ? (lineCount * fontOpts.lineHeight) + (opts.padding * 2) : 0;

            if (me.isHorizontal()) {
                minSize.width = me.maxWidth; // fill all the width
                minSize.height = textSize;
            } else {
                minSize.width = textSize;
                minSize.height = me.maxHeight; // fill all the height
            }

            me.width = minSize.width;
            me.height = minSize.height;

        },
        afterFit: noop$2,

        // Shared Methods
        isHorizontal: function () {
            var pos = this.options.position;
            return pos === 'top' || pos === 'bottom';
        },

        // Actually draw the title block on the canvas
        draw: function () {
            var me = this;
            var ctx = me.ctx;
            var opts = me.options;

            if (opts.display) {
                var fontOpts = helpers$1.options._parseFont(opts);
                var lineHeight = fontOpts.lineHeight;
                var offset = lineHeight / 2 + opts.padding;
                var rotation = 0;
                var top = me.top;
                var left = me.left;
                var bottom = me.bottom;
                var right = me.right;
                var maxWidth, titleX, titleY;

                ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour
                ctx.font = fontOpts.string;

                // Horizontal
                if (me.isHorizontal()) {
                    titleX = left + ((right - left) / 2); // midpoint of the width
                    titleY = top + offset;
                    maxWidth = right - left;
                } else {
                    titleX = opts.position === 'left' ? left + offset : right - offset;
                    titleY = top + ((bottom - top) / 2);
                    maxWidth = bottom - top;
                    rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
                }

                ctx.save();
                ctx.translate(titleX, titleY);
                ctx.rotate(rotation);
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';

                var text = opts.text;
                if (helpers$1.isArray(text)) {
                    var y = 0;
                    for (var i = 0; i < text.length; ++i) {
                        ctx.fillText(text[i], 0, y, maxWidth);
                        y += lineHeight;
                    }
                } else {
                    ctx.fillText(text, 0, 0, maxWidth);
                }

                ctx.restore();
            }
        }
    });

    function createNewTitleBlockAndAttach(chart, titleOpts) {
        var title = new Title({
            ctx: chart.ctx,
            options: titleOpts,
            chart: chart
        });

        core_layouts.configure(chart, title, titleOpts);
        core_layouts.addBox(chart, title);
        chart.titleBlock = title;
    }

    var plugin_title = {
        id: 'title',

        /**
         * Backward compatibility: since 2.1.5, the title is registered as a plugin, making
         * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
         * the plugin, which one will be re-exposed in the chart.js file.
         * https://github.com/chartjs/Chart.js/pull/2640
         * @private
         */
        _element: Title,

        beforeInit: function (chart) {
            var titleOpts = chart.options.title;

            if (titleOpts) {
                createNewTitleBlockAndAttach(chart, titleOpts);
            }
        },

        beforeUpdate: function (chart) {
            var titleOpts = chart.options.title;
            var titleBlock = chart.titleBlock;

            if (titleOpts) {
                helpers$1.mergeIf(titleOpts, core_defaults.global.title);

                if (titleBlock) {
                    core_layouts.configure(chart, titleBlock, titleOpts);
                    titleBlock.options = titleOpts;
                } else {
                    createNewTitleBlockAndAttach(chart, titleOpts);
                }
            } else if (titleBlock) {
                core_layouts.removeBox(chart, titleBlock);
                delete chart.titleBlock;
            }
        }
    };

    var plugins = {};
    var filler = plugin_filler;
    var legend = plugin_legend;
    var title = plugin_title;
    plugins.filler = filler;
    plugins.legend = legend;
    plugins.title = title;

    /**
     * @namespace Chart
     */


    core_controller.helpers = helpers$1;

    // @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
    core_helpers(core_controller);

    core_controller._adapters = core_adapters;
    core_controller.Animation = core_animation;
    core_controller.animationService = core_animations;
    core_controller.controllers = controllers;
    core_controller.DatasetController = core_datasetController;
    core_controller.defaults = core_defaults;
    core_controller.Element = core_element;
    core_controller.elements = elements;
    core_controller.Interaction = core_interaction;
    core_controller.layouts = core_layouts;
    core_controller.platform = platform;
    core_controller.plugins = core_plugins;
    core_controller.Scale = core_scale;
    core_controller.scaleService = core_scaleService;
    core_controller.Ticks = core_ticks;
    core_controller.Tooltip = core_tooltip;

    // Register built-in scales

    core_controller.helpers.each(scales, function (scale, type) {
        core_controller.scaleService.registerScaleType(type, scale, scale._defaults);
    });

    // Load to register built-in adapters (as side effects)


    // Loading built-in plugins

    for (var k in plugins) {
        if (plugins.hasOwnProperty(k)) {
            core_controller.plugins.register(plugins[k]);
        }
    }

    core_controller.platform.initialize();

    var src = core_controller;
    if (typeof window !== 'undefined') {
        window.Chart = core_controller;
    }

    // DEPRECATIONS

    /**
     * Provided for backward compatibility, not available anymore
     * @namespace Chart.Chart
     * @deprecated since version 2.8.0
     * @todo remove at version 3
     * @private
     */
    core_controller.Chart = core_controller;

    /**
     * Provided for backward compatibility, not available anymore
     * @namespace Chart.Legend
     * @deprecated since version 2.1.5
     * @todo remove at version 3
     * @private
     */
    core_controller.Legend = plugins.legend._element;

    /**
     * Provided for backward compatibility, not available anymore
     * @namespace Chart.Title
     * @deprecated since version 2.1.5
     * @todo remove at version 3
     * @private
     */
    core_controller.Title = plugins.title._element;

    /**
     * Provided for backward compatibility, use Chart.plugins instead
     * @namespace Chart.pluginService
     * @deprecated since version 2.1.5
     * @todo remove at version 3
     * @private
     */
    core_controller.pluginService = core_controller.plugins;

    /**
     * Provided for backward compatibility, inheriting from Chart.PlugingBase has no
     * effect, instead simply create/register plugins via plain JavaScript objects.
     * @interface Chart.PluginBase
     * @deprecated since version 2.5.0
     * @todo remove at version 3
     * @private
     */
    core_controller.PluginBase = core_controller.Element.extend({});

    /**
     * Provided for backward compatibility, use Chart.helpers.canvas instead.
     * @namespace Chart.canvasHelpers
     * @deprecated since version 2.6.0
     * @todo remove at version 3
     * @private
     */
    core_controller.canvasHelpers = core_controller.helpers.canvas;

    /**
     * Provided for backward compatibility, use Chart.layouts instead.
     * @namespace Chart.layoutService
     * @deprecated since version 2.7.3
     * @todo remove at version 3
     * @private
     */
    core_controller.layoutService = core_controller.layouts;

    /**
     * Provided for backward compatibility, not available anymore.
     * @namespace Chart.LinearScaleBase
     * @deprecated since version 2.8
     * @todo remove at version 3
     * @private
     */
    core_controller.LinearScaleBase = scale_linearbase;

    /**
     * Provided for backward compatibility, instead we should create a new Chart
     * by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`).
     * @deprecated since version 2.8.0
     * @todo remove at version 3
     */
    core_controller.helpers.each(
        [
            'Bar',
            'Bubble',
            'Doughnut',
            'Line',
            'PolarArea',
            'Radar',
            'Scatter'
        ],
        function (klass) {
            core_controller[klass] = function (ctx, cfg) {
                return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, {
                    type: klass.charAt(0).toLowerCase() + klass.slice(1)
                }));
            };
        }
    );

    return src;

})));
;
(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.Dygraph = 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 (require, module, exports) {
            // shim for using process in browser
            var process = module.exports = {};

            // cached from whatever global is present so that test runners that stub it
            // don't break things.  But we need to wrap it in a try catch in case it is
            // wrapped in strict mode code which doesn't define any globals.  It's inside a
            // function because try/catches deoptimize in certain engines.

            var cachedSetTimeout;
            var cachedClearTimeout;

            function defaultSetTimout() {
                throw new Error('setTimeout has not been defined');
            }
            function defaultClearTimeout() {
                throw new Error('clearTimeout has not been defined');
            }
            (function () {
                try {
                    if (typeof setTimeout === 'function') {
                        cachedSetTimeout = setTimeout;
                    } else {
                        cachedSetTimeout = defaultSetTimout;
                    }
                } catch (e) {
                    cachedSetTimeout = defaultSetTimout;
                }
                try {
                    if (typeof clearTimeout === 'function') {
                        cachedClearTimeout = clearTimeout;
                    } else {
                        cachedClearTimeout = defaultClearTimeout;
                    }
                } catch (e) {
                    cachedClearTimeout = defaultClearTimeout;
                }
            }())
            function runTimeout(fun) {
                if (cachedSetTimeout === setTimeout) {
                    //normal enviroments in sane situations
                    return setTimeout(fun, 0);
                }
                // if setTimeout wasn't available but was latter defined
                if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
                    cachedSetTimeout = setTimeout;
                    return setTimeout(fun, 0);
                }
                try {
                    // when when somebody has screwed with setTimeout but no I.E. maddness
                    return cachedSetTimeout(fun, 0);
                } catch (e) {
                    try {
                        // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                        return cachedSetTimeout.call(null, fun, 0);
                    } catch (e) {
                        // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                        return cachedSetTimeout.call(this, fun, 0);
                    }
                }


            }
            function runClearTimeout(marker) {
                if (cachedClearTimeout === clearTimeout) {
                    //normal enviroments in sane situations
                    return clearTimeout(marker);
                }
                // if clearTimeout wasn't available but was latter defined
                if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
                    cachedClearTimeout = clearTimeout;
                    return clearTimeout(marker);
                }
                try {
                    // when when somebody has screwed with setTimeout but no I.E. maddness
                    return cachedClearTimeout(marker);
                } catch (e) {
                    try {
                        // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                        return cachedClearTimeout.call(null, marker);
                    } catch (e) {
                        // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                        // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                        return cachedClearTimeout.call(this, marker);
                    }
                }



            }
            var queue = [];
            var draining = false;
            var currentQueue;
            var queueIndex = -1;

            function cleanUpNextTick() {
                if (!draining || !currentQueue) {
                    return;
                }
                draining = false;
                if (currentQueue.length) {
                    queue = currentQueue.concat(queue);
                } else {
                    queueIndex = -1;
                }
                if (queue.length) {
                    drainQueue();
                }
            }

            function drainQueue() {
                if (draining) {
                    return;
                }
                var timeout = runTimeout(cleanUpNextTick);
                draining = true;

                var len = queue.length;
                while (len) {
                    currentQueue = queue;
                    queue = [];
                    while (++queueIndex < len) {
                        if (currentQueue) {
                            currentQueue[queueIndex].run();
                        }
                    }
                    queueIndex = -1;
                    len = queue.length;
                }
                currentQueue = null;
                draining = false;
                runClearTimeout(timeout);
            }

            process.nextTick = function (fun) {
                var args = new Array(arguments.length - 1);
                if (arguments.length > 1) {
                    for (var i = 1; i < arguments.length; i++) {
                        args[i - 1] = arguments[i];
                    }
                }
                queue.push(new Item(fun, args));
                if (queue.length === 1 && !draining) {
                    runTimeout(drainQueue);
                }
            };

            // v8 likes predictible objects
            function Item(fun, array) {
                this.fun = fun;
                this.array = array;
            }
            Item.prototype.run = function () {
                this.fun.apply(null, this.array);
            };
            process.title = 'browser';
            process.browser = true;
            process.env = {};
            process.argv = [];
            process.version = ''; // empty string to avoid regexp issues
            process.versions = {};

            function noop() { }

            process.on = noop;
            process.addListener = noop;
            process.once = noop;
            process.off = noop;
            process.removeListener = noop;
            process.removeAllListeners = noop;
            process.emit = noop;
            process.prependListener = noop;
            process.prependOnceListener = noop;

            process.listeners = function (name) { return [] }

            process.binding = function (name) {
                throw new Error('process.binding is not supported');
            };

            process.cwd = function () { return '/' };
            process.chdir = function (dir) {
                throw new Error('process.chdir is not supported');
            };
            process.umask = function () { return 0; };

        }, {}], 2: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview DataHandler implementation for the custom bars option.
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            var _bars = require('./bars');

            var _bars2 = _interopRequireDefault(_bars);

            /**
             * @constructor
             * @extends Dygraph.DataHandlers.BarsHandler
             */
            var CustomBarsHandler = function CustomBarsHandler() { };

            CustomBarsHandler.prototype = new _bars2['default']();

            /** @inheritDoc */
            CustomBarsHandler.prototype.extractSeries = function (rawData, i, options) {
                // TODO(danvk): pre-allocate series here.
                var series = [];
                var x, y, point;
                var logScale = options.get('logscale');
                for (var j = 0; j < rawData.length; j++) {
                    x = rawData[j][0];
                    point = rawData[j][i];
                    if (logScale && point !== null) {
                        // On the log scale, points less than zero do not exist.
                        // This will create a gap in the chart.
                        if (point[0] <= 0 || point[1] <= 0 || point[2] <= 0) {
                            point = null;
                        }
                    }
                    // Extract to the unified data format.
                    if (point !== null) {
                        y = point[1];
                        if (y !== null && !isNaN(y)) {
                            series.push([x, y, [point[0], point[2]]]);
                        } else {
                            series.push([x, y, [y, y]]);
                        }
                    } else {
                        series.push([x, null, [null, null]]);
                    }
                }
                return series;
            };

            /** @inheritDoc */
            CustomBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) {
                rollPeriod = Math.min(rollPeriod, originalData.length);
                var rollingData = [];
                var y, low, high, mid, count, i, extremes;

                low = 0;
                mid = 0;
                high = 0;
                count = 0;
                for (i = 0; i < originalData.length; i++) {
                    y = originalData[i][1];
                    extremes = originalData[i][2];
                    rollingData[i] = originalData[i];

                    if (y !== null && !isNaN(y)) {
                        low += extremes[0];
                        mid += y;
                        high += extremes[1];
                        count += 1;
                    }
                    if (i - rollPeriod >= 0) {
                        var prev = originalData[i - rollPeriod];
                        if (prev[1] !== null && !isNaN(prev[1])) {
                            low -= prev[2][0];
                            mid -= prev[1];
                            high -= prev[2][1];
                            count -= 1;
                        }
                    }
                    if (count) {
                        rollingData[i] = [originalData[i][0], 1.0 * mid / count, [1.0 * low / count, 1.0 * high / count]];
                    } else {
                        rollingData[i] = [originalData[i][0], null, [null, null]];
                    }
                }

                return rollingData;
            };

            exports['default'] = CustomBarsHandler;
            module.exports = exports['default'];

        }, { "./bars": 5 }], 3: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview DataHandler implementation for the error bars option.
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, "__esModule", {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

            var _bars = require('./bars');

            var _bars2 = _interopRequireDefault(_bars);

            /**
             * @constructor
             * @extends BarsHandler
             */
            var ErrorBarsHandler = function ErrorBarsHandler() { };

            ErrorBarsHandler.prototype = new _bars2["default"]();

            /** @inheritDoc */
            ErrorBarsHandler.prototype.extractSeries = function (rawData, i, options) {
                // TODO(danvk): pre-allocate series here.
                var series = [];
                var x, y, variance, point;
                var sigma = options.get("sigma");
                var logScale = options.get('logscale');
                for (var j = 0; j < rawData.length; j++) {
                    x = rawData[j][0];
                    point = rawData[j][i];
                    if (logScale && point !== null) {
                        // On the log scale, points less than zero do not exist.
                        // This will create a gap in the chart.
                        if (point[0] <= 0 || point[0] - sigma * point[1] <= 0) {
                            point = null;
                        }
                    }
                    // Extract to the unified data format.
                    if (point !== null) {
                        y = point[0];
                        if (y !== null && !isNaN(y)) {
                            variance = sigma * point[1];
                            // preserve original error value in extras for further
                            // filtering
                            series.push([x, y, [y - variance, y + variance, point[1]]]);
                        } else {
                            series.push([x, y, [y, y, y]]);
                        }
                    } else {
                        series.push([x, null, [null, null, null]]);
                    }
                }
                return series;
            };

            /** @inheritDoc */
            ErrorBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) {
                rollPeriod = Math.min(rollPeriod, originalData.length);
                var rollingData = [];
                var sigma = options.get("sigma");

                var i, j, y, v, sum, num_ok, stddev, variance, value;

                // Calculate the rolling average for the first rollPeriod - 1 points
                // where there is not enough data to roll over the full number of points
                for (i = 0; i < originalData.length; i++) {
                    sum = 0;
                    variance = 0;
                    num_ok = 0;
                    for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
                        y = originalData[j][1];
                        if (y === null || isNaN(y)) continue;
                        num_ok++;
                        sum += y;
                        variance += Math.pow(originalData[j][2][2], 2);
                    }
                    if (num_ok) {
                        stddev = Math.sqrt(variance) / num_ok;
                        value = sum / num_ok;
                        rollingData[i] = [originalData[i][0], value, [value - sigma * stddev, value + sigma * stddev]];
                    } else {
                        // This explicitly preserves NaNs to aid with "independent
                        // series".
                        // See testRollingAveragePreservesNaNs.
                        v = rollPeriod == 1 ? originalData[i][1] : null;
                        rollingData[i] = [originalData[i][0], v, [v, v]];
                    }
                }

                return rollingData;
            };

            exports["default"] = ErrorBarsHandler;
            module.exports = exports["default"];

        }, { "./bars": 5 }], 4: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview DataHandler implementation for the combination 
             * of error bars and fractions options.
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, "__esModule", {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

            var _bars = require('./bars');

            var _bars2 = _interopRequireDefault(_bars);

            /**
             * @constructor
             * @extends Dygraph.DataHandlers.BarsHandler
             */
            var FractionsBarsHandler = function FractionsBarsHandler() { };

            FractionsBarsHandler.prototype = new _bars2["default"]();

            /** @inheritDoc */
            FractionsBarsHandler.prototype.extractSeries = function (rawData, i, options) {
                // TODO(danvk): pre-allocate series here.
                var series = [];
                var x, y, point, num, den, value, stddev, variance;
                var mult = 100.0;
                var sigma = options.get("sigma");
                var logScale = options.get('logscale');
                for (var j = 0; j < rawData.length; j++) {
                    x = rawData[j][0];
                    point = rawData[j][i];
                    if (logScale && point !== null) {
                        // On the log scale, points less than zero do not exist.
                        // This will create a gap in the chart.
                        if (point[0] <= 0 || point[1] <= 0) {
                            point = null;
                        }
                    }
                    // Extract to the unified data format.
                    if (point !== null) {
                        num = point[0];
                        den = point[1];
                        if (num !== null && !isNaN(num)) {
                            value = den ? num / den : 0.0;
                            stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
                            variance = mult * stddev;
                            y = mult * value;
                            // preserve original values in extras for further filtering
                            series.push([x, y, [y - variance, y + variance, num, den]]);
                        } else {
                            series.push([x, num, [num, num, num, den]]);
                        }
                    } else {
                        series.push([x, null, [null, null, null, null]]);
                    }
                }
                return series;
            };

            /** @inheritDoc */
            FractionsBarsHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) {
                rollPeriod = Math.min(rollPeriod, originalData.length);
                var rollingData = [];
                var sigma = options.get("sigma");
                var wilsonInterval = options.get("wilsonInterval");

                var low, high, i, stddev;
                var num = 0;
                var den = 0; // numerator/denominator
                var mult = 100.0;
                for (i = 0; i < originalData.length; i++) {
                    num += originalData[i][2][2];
                    den += originalData[i][2][3];
                    if (i - rollPeriod >= 0) {
                        num -= originalData[i - rollPeriod][2][2];
                        den -= originalData[i - rollPeriod][2][3];
                    }

                    var date = originalData[i][0];
                    var value = den ? num / den : 0.0;
                    if (wilsonInterval) {
                        // For more details on this confidence interval, see:
                        // http://en.wikipedia.org/wiki/Binomial_confidence_interval
                        if (den) {
                            var p = value < 0 ? 0 : value,
                                n = den;
                            var pm = sigma * Math.sqrt(p * (1 - p) / n + sigma * sigma / (4 * n * n));
                            var denom = 1 + sigma * sigma / den;
                            low = (p + sigma * sigma / (2 * den) - pm) / denom;
                            high = (p + sigma * sigma / (2 * den) + pm) / denom;
                            rollingData[i] = [date, p * mult, [low * mult, high * mult]];
                        } else {
                            rollingData[i] = [date, 0, [0, 0]];
                        }
                    } else {
                        stddev = den ? sigma * Math.sqrt(value * (1 - value) / den) : 1.0;
                        rollingData[i] = [date, mult * value, [mult * (value - stddev), mult * (value + stddev)]];
                    }
                }

                return rollingData;
            };

            exports["default"] = FractionsBarsHandler;
            module.exports = exports["default"];

        }, { "./bars": 5 }], 5: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview DataHandler base implementation for the "bar" 
             * data formats. This implementation must be extended and the
             * extractSeries and rollingAverage must be implemented.
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             */

            /*global Dygraph:false */
            /*global DygraphLayout:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            var _datahandler = require('./datahandler');

            var _datahandler2 = _interopRequireDefault(_datahandler);

            var _dygraphLayout = require('../dygraph-layout');

            var _dygraphLayout2 = _interopRequireDefault(_dygraphLayout);

            /**
             * @constructor
             * @extends {Dygraph.DataHandler}
             */
            var BarsHandler = function BarsHandler() {
                _datahandler2['default'].call(this);
            };
            BarsHandler.prototype = new _datahandler2['default']();

            // TODO(danvk): figure out why the jsdoc has to be copy/pasted from superclass.
            //   (I get closure compiler errors if this isn't here.)
            /**
             * @override
             * @param {!Array.<Array>} rawData The raw data passed into dygraphs where 
             *     rawData[i] = [x,ySeries1,...,ySeriesN].
             * @param {!number} seriesIndex Index of the series to extract. All other
             *     series should be ignored.
             * @param {!DygraphOptions} options Dygraph options.
             * @return {Array.<[!number,?number,?]>} The series in the unified data format
             *     where series[i] = [x,y,{extras}]. 
             */
            BarsHandler.prototype.extractSeries = function (rawData, seriesIndex, options) {
                // Not implemented here must be extended
            };

            /**
             * @override
             * @param {!Array.<[!number,?number,?]>} series The series in the unified 
             *          data format where series[i] = [x,y,{extras}].
             * @param {!number} rollPeriod The number of points over which to average the data
             * @param {!DygraphOptions} options The dygraph options.
             * TODO(danvk): be more specific than "Array" here.
             * @return {!Array.<[!number,?number,?]>} the rolled series.
             */
            BarsHandler.prototype.rollingAverage = function (series, rollPeriod, options) {
                // Not implemented here, must be extended.
            };

            /** @inheritDoc */
            BarsHandler.prototype.onPointsCreated_ = function (series, points) {
                for (var i = 0; i < series.length; ++i) {
                    var item = series[i];
                    var point = points[i];
                    point.y_top = NaN;
                    point.y_bottom = NaN;
                    point.yval_minus = _datahandler2['default'].parseFloat(item[2][0]);
                    point.yval_plus = _datahandler2['default'].parseFloat(item[2][1]);
                }
            };

            /** @inheritDoc */
            BarsHandler.prototype.getExtremeYValues = function (series, dateWindow, options) {
                var minY = null,
                    maxY = null,
                    y;

                var firstIdx = 0;
                var lastIdx = series.length - 1;

                for (var j = firstIdx; j <= lastIdx; j++) {
                    y = series[j][1];
                    if (y === null || isNaN(y)) continue;

                    var low = series[j][2][0];
                    var high = series[j][2][1];

                    if (low > y) low = y; // this can happen with custom bars,
                    if (high < y) high = y; // e.g. in tests/custom-bars.html

                    if (maxY === null || high > maxY) maxY = high;
                    if (minY === null || low < minY) minY = low;
                }

                return [minY, maxY];
            };

            /** @inheritDoc */
            BarsHandler.prototype.onLineEvaluated = function (points, axis, logscale) {
                var point;
                for (var j = 0; j < points.length; j++) {
                    // Copy over the error terms
                    point = points[j];
                    point.y_top = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_minus, logscale);
                    point.y_bottom = _dygraphLayout2['default'].calcYNormal_(axis, point.yval_plus, logscale);
                }
            };

            exports['default'] = BarsHandler;
            module.exports = exports['default'];

        }, { "../dygraph-layout": 13, "./datahandler": 6 }], 6: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview This file contains the managment of data handlers
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             *
             * The idea is to define a common, generic data format that works for all data
             * structures supported by dygraphs. To make this possible, the DataHandler
             * interface is introduced. This makes it possible, that dygraph itself can work
             * with the same logic for every data type independent of the actual format and
             * the DataHandler takes care of the data format specific jobs.
             * DataHandlers are implemented for all data types supported by Dygraphs and
             * return Dygraphs compliant formats.
             * By default the correct DataHandler is chosen based on the options set.
             * Optionally the user may use his own DataHandler (similar to the plugin
             * system).
             *
             *
             * The unified data format returend by each handler is defined as so:
             * series[n][point] = [x,y,(extras)]
             *
             * This format contains the common basis that is needed to draw a simple line
             * series extended by optional extras for more complex graphing types. It
             * contains a primitive x value as first array entry, a primitive y value as
             * second array entry and an optional extras object for additional data needed.
             *
             * x must always be a number.
             * y must always be a number, NaN of type number or null.
             * extras is optional and must be interpreted by the DataHandler. It may be of
             * any type.
             *
             * In practice this might look something like this:
             * default: [x, yVal]
             * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ]
             *
             */
            /*global Dygraph:false */
            /*global DygraphLayout:false */

            "use strict";

            /**
             *
             * The data handler is responsible for all data specific operations. All of the
             * series data it receives and returns is always in the unified data format.
             * Initially the unified data is created by the extractSeries method
             * @constructor
             */
            Object.defineProperty(exports, "__esModule", {
                value: true
            });
            var DygraphDataHandler = function DygraphDataHandler() { };

            var handler = DygraphDataHandler;

            /**
             * X-value array index constant for unified data samples.
             * @const
             * @type {number}
             */
            handler.X = 0;

            /**
             * Y-value array index constant for unified data samples.
             * @const
             * @type {number}
             */
            handler.Y = 1;

            /**
             * Extras-value array index constant for unified data samples.
             * @const
             * @type {number}
             */
            handler.EXTRAS = 2;

            /**
             * Extracts one series from the raw data (a 2D array) into an array of the
             * unified data format.
             * This is where undesirable points (i.e. negative values on log scales and
             * missing values through which we wish to connect lines) are dropped.
             * TODO(danvk): the "missing values" bit above doesn't seem right.
             *
             * @param {!Array.<Array>} rawData The raw data passed into dygraphs where
             *     rawData[i] = [x,ySeries1,...,ySeriesN].
             * @param {!number} seriesIndex Index of the series to extract. All other
             *     series should be ignored.
             * @param {!DygraphOptions} options Dygraph options.
             * @return {Array.<[!number,?number,?]>} The series in the unified data format
             *     where series[i] = [x,y,{extras}].
             */
            handler.prototype.extractSeries = function (rawData, seriesIndex, options) { };

            /**
             * Converts a series to a Point array.  The resulting point array must be
             * returned in increasing order of idx property.
             *
             * @param {!Array.<[!number,?number,?]>} series The series in the unified
             *          data format where series[i] = [x,y,{extras}].
             * @param {!string} setName Name of the series.
             * @param {!number} boundaryIdStart Index offset of the first point, equal to the
             *          number of skipped points left of the date window minimum (if any).
             * @return {!Array.<Dygraph.PointType>} List of points for this series.
             */
            handler.prototype.seriesToPoints = function (series, setName, boundaryIdStart) {
                // TODO(bhs): these loops are a hot-spot for high-point-count charts. In
                // fact,
                // on chrome+linux, they are 6 times more expensive than iterating through
                // the
                // points and drawing the lines. The brunt of the cost comes from allocating
                // the |point| structures.
                var points = [];
                for (var i = 0; i < series.length; ++i) {
                    var item = series[i];
                    var yraw = item[1];
                    var yval = yraw === null ? null : handler.parseFloat(yraw);
                    var point = {
                        x: NaN,
                        y: NaN,
                        xval: handler.parseFloat(item[0]),
                        yval: yval,
                        name: setName, // TODO(danvk): is this really necessary?
                        idx: i + boundaryIdStart
                    };
                    points.push(point);
                }
                this.onPointsCreated_(series, points);
                return points;
            };

            /**
             * Callback called for each series after the series points have been generated
             * which will later be used by the plotters to draw the graph.
             * Here data may be added to the seriesPoints which is needed by the plotters.
             * The indexes of series and points are in sync meaning the original data
             * sample for series[i] is points[i].
             *
             * @param {!Array.<[!number,?number,?]>} series The series in the unified
             *     data format where series[i] = [x,y,{extras}].
             * @param {!Array.<Dygraph.PointType>} points The corresponding points passed
             *     to the plotter.
             * @protected
             */
            handler.prototype.onPointsCreated_ = function (series, points) { };

            /**
             * Calculates the rolling average of a data set.
             *
             * @param {!Array.<[!number,?number,?]>} series The series in the unified
             *          data format where series[i] = [x,y,{extras}].
             * @param {!number} rollPeriod The number of points over which to average the data
             * @param {!DygraphOptions} options The dygraph options.
             * @return {!Array.<[!number,?number,?]>} the rolled series.
             */
            handler.prototype.rollingAverage = function (series, rollPeriod, options) { };

            /**
             * Computes the range of the data series (including confidence intervals).
             *
             * @param {!Array.<[!number,?number,?]>} series The series in the unified
             *     data format where series[i] = [x, y, {extras}].
             * @param {!Array.<number>} dateWindow The x-value range to display with
             *     the format: [min, max].
             * @param {!DygraphOptions} options The dygraph options.
             * @return {Array.<number>} The low and high extremes of the series in the
             *     given window with the format: [low, high].
             */
            handler.prototype.getExtremeYValues = function (series, dateWindow, options) { };

            /**
             * Callback called for each series after the layouting data has been
             * calculated before the series is drawn. Here normalized positioning data
             * should be calculated for the extras of each point.
             *
             * @param {!Array.<Dygraph.PointType>} points The points passed to
             *          the plotter.
             * @param {!Object} axis The axis on which the series will be plotted.
             * @param {!boolean} logscale Weather or not to use a logscale.
             */
            handler.prototype.onLineEvaluated = function (points, axis, logscale) { };

            /**
             * Optimized replacement for parseFloat, which was way too slow when almost
             * all values were type number, with few edge cases, none of which were strings.
             * @param {?number} val
             * @return {number}
             * @protected
             */
            handler.parseFloat = function (val) {
                // parseFloat(null) is NaN
                if (val === null) {
                    return NaN;
                }

                // Assume it's a number or NaN. If it's something else, I'll be shocked.
                return val;
            };

            exports["default"] = DygraphDataHandler;
            module.exports = exports["default"];

        }, {}], 7: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview DataHandler implementation for the fractions option.
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            var _datahandler = require('./datahandler');

            var _datahandler2 = _interopRequireDefault(_datahandler);

            var _default = require('./default');

            var _default2 = _interopRequireDefault(_default);

            /**
             * @extends DefaultHandler
             * @constructor
             */
            var DefaultFractionHandler = function DefaultFractionHandler() { };

            DefaultFractionHandler.prototype = new _default2['default']();

            DefaultFractionHandler.prototype.extractSeries = function (rawData, i, options) {
                // TODO(danvk): pre-allocate series here.
                var series = [];
                var x, y, point, num, den, value;
                var mult = 100.0;
                var logScale = options.get('logscale');
                for (var j = 0; j < rawData.length; j++) {
                    x = rawData[j][0];
                    point = rawData[j][i];
                    if (logScale && point !== null) {
                        // On the log scale, points less than zero do not exist.
                        // This will create a gap in the chart.
                        if (point[0] <= 0 || point[1] <= 0) {
                            point = null;
                        }
                    }
                    // Extract to the unified data format.
                    if (point !== null) {
                        num = point[0];
                        den = point[1];
                        if (num !== null && !isNaN(num)) {
                            value = den ? num / den : 0.0;
                            y = mult * value;
                            // preserve original values in extras for further filtering
                            series.push([x, y, [num, den]]);
                        } else {
                            series.push([x, num, [num, den]]);
                        }
                    } else {
                        series.push([x, null, [null, null]]);
                    }
                }
                return series;
            };

            DefaultFractionHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) {
                rollPeriod = Math.min(rollPeriod, originalData.length);
                var rollingData = [];

                var i;
                var num = 0;
                var den = 0; // numerator/denominator
                var mult = 100.0;
                for (i = 0; i < originalData.length; i++) {
                    num += originalData[i][2][0];
                    den += originalData[i][2][1];
                    if (i - rollPeriod >= 0) {
                        num -= originalData[i - rollPeriod][2][0];
                        den -= originalData[i - rollPeriod][2][1];
                    }

                    var date = originalData[i][0];
                    var value = den ? num / den : 0.0;
                    rollingData[i] = [date, mult * value];
                }

                return rollingData;
            };

            exports['default'] = DefaultFractionHandler;
            module.exports = exports['default'];

        }, { "./datahandler": 6, "./default": 8 }], 8: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview DataHandler default implementation used for simple line charts.
             * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            var _datahandler = require('./datahandler');

            var _datahandler2 = _interopRequireDefault(_datahandler);

            /**
             * @constructor
             * @extends Dygraph.DataHandler
             */
            var DefaultHandler = function DefaultHandler() { };

            DefaultHandler.prototype = new _datahandler2['default']();

            /** @inheritDoc */
            DefaultHandler.prototype.extractSeries = function (rawData, i, options) {
                // TODO(danvk): pre-allocate series here.
                var series = [];
                var logScale = options.get('logscale');
                for (var j = 0; j < rawData.length; j++) {
                    var x = rawData[j][0];
                    var point = rawData[j][i];
                    if (logScale) {
                        // On the log scale, points less than zero do not exist.
                        // This will create a gap in the chart.
                        if (point <= 0) {
                            point = null;
                        }
                    }
                    series.push([x, point]);
                }
                return series;
            };

            /** @inheritDoc */
            DefaultHandler.prototype.rollingAverage = function (originalData, rollPeriod, options) {
                rollPeriod = Math.min(rollPeriod, originalData.length);
                var rollingData = [];

                var i, j, y, sum, num_ok;
                // Calculate the rolling average for the first rollPeriod - 1 points
                // where
                // there is not enough data to roll over the full number of points
                if (rollPeriod == 1) {
                    return originalData;
                }
                for (i = 0; i < originalData.length; i++) {
                    sum = 0;
                    num_ok = 0;
                    for (j = Math.max(0, i - rollPeriod + 1); j < i + 1; j++) {
                        y = originalData[j][1];
                        if (y === null || isNaN(y)) continue;
                        num_ok++;
                        sum += originalData[j][1];
                    }
                    if (num_ok) {
                        rollingData[i] = [originalData[i][0], sum / num_ok];
                    } else {
                        rollingData[i] = [originalData[i][0], null];
                    }
                }

                return rollingData;
            };

            /** @inheritDoc */
            DefaultHandler.prototype.getExtremeYValues = function (series, dateWindow, options) {
                var minY = null,
                    maxY = null,
                    y;
                var firstIdx = 0,
                    lastIdx = series.length - 1;

                for (var j = firstIdx; j <= lastIdx; j++) {
                    y = series[j][1];
                    if (y === null || isNaN(y)) continue;
                    if (maxY === null || y > maxY) {
                        maxY = y;
                    }
                    if (minY === null || y < minY) {
                        minY = y;
                    }
                }
                return [minY, maxY];
            };

            exports['default'] = DefaultHandler;
            module.exports = exports['default'];

        }, { "./datahandler": 6 }], 9: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview Based on PlotKit.CanvasRenderer, but modified to meet the
             * needs of dygraphs.
             *
             * In particular, support for:
             * - grid overlays
             * - error bars
             * - dygraphs attribute system
             */

            /**
             * The DygraphCanvasRenderer class does the actual rendering of the chart onto
             * a canvas. It's based on PlotKit.CanvasRenderer.
             * @param {Object} element The canvas to attach to
             * @param {Object} elementContext The 2d context of the canvas (injected so it
             * can be mocked for testing.)
             * @param {Layout} layout The DygraphLayout object for this graph.
             * @constructor
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

            var _dygraphUtils = require('./dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            var _dygraph = require('./dygraph');

            var _dygraph2 = _interopRequireDefault(_dygraph);

            /**
             * @constructor
             *
             * This gets called when there are "new points" to chart. This is generally the
             * case when the underlying data being charted has changed. It is _not_ called
             * in the common case that the user has zoomed or is panning the view.
             *
             * The chart canvas has already been created by the Dygraph object. The
             * renderer simply gets a drawing context.
             *
             * @param {Dygraph} dygraph The chart to which this renderer belongs.
             * @param {HTMLCanvasElement} element The &lt;canvas&gt; DOM element on which to draw.
             * @param {CanvasRenderingContext2D} elementContext The drawing context.
             * @param {DygraphLayout} layout The chart's DygraphLayout object.
             *
             * TODO(danvk): remove the elementContext property.
             */
            var DygraphCanvasRenderer = function DygraphCanvasRenderer(dygraph, element, elementContext, layout) {
                this.dygraph_ = dygraph;

                this.layout = layout;
                this.element = element;
                this.elementContext = elementContext;

                this.height = dygraph.height_;
                this.width = dygraph.width_;

                // --- check whether everything is ok before we return
                if (!utils.isCanvasSupported(this.element)) {
                    throw "Canvas is not supported.";
                }

                // internal state
                this.area = layout.getPlotArea();

                // Set up a clipping area for the canvas (and the interaction canvas).
                // This ensures that we don't overdraw.
                var ctx = this.dygraph_.canvas_ctx_;
                ctx.beginPath();
                ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
                ctx.clip();

                ctx = this.dygraph_.hidden_ctx_;
                ctx.beginPath();
                ctx.rect(this.area.x, this.area.y, this.area.w, this.area.h);
                ctx.clip();
            };

            /**
             * Clears out all chart content and DOM elements.
             * This is called immediately before render() on every frame, including
             * during zooms and pans.
             * @private
             */
            DygraphCanvasRenderer.prototype.clear = function () {
                this.elementContext.clearRect(0, 0, this.width, this.height);
            };

            /**
             * This method is responsible for drawing everything on the chart, including
             * lines, error bars, fills and axes.
             * It is called immediately after clear() on every frame, including during pans
             * and zooms.
             * @private
             */
            DygraphCanvasRenderer.prototype.render = function () {
                // attaches point.canvas{x,y}
                this._updatePoints();

                // actually draws the chart.
                this._renderLineChart();
            };

            /**
             * Returns a predicate to be used with an iterator, which will
             * iterate over points appropriately, depending on whether
             * connectSeparatedPoints is true. When it's false, the predicate will
             * skip over points with missing yVals.
             */
            DygraphCanvasRenderer._getIteratorPredicate = function (connectSeparatedPoints) {
                return connectSeparatedPoints ? DygraphCanvasRenderer._predicateThatSkipsEmptyPoints : null;
            };

            DygraphCanvasRenderer._predicateThatSkipsEmptyPoints = function (array, idx) {
                return array[idx].yval !== null;
            };

            /**
             * Draws a line with the styles passed in and calls all the drawPointCallbacks.
             * @param {Object} e The dictionary passed to the plotter function.
             * @private
             */
            DygraphCanvasRenderer._drawStyledLine = function (e, color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize) {
                var g = e.dygraph;
                // TODO(konigsberg): Compute attributes outside this method call.
                var stepPlot = g.getBooleanOption("stepPlot", e.setName);

                if (!utils.isArrayLike(strokePattern)) {
                    strokePattern = null;
                }

                var drawGapPoints = g.getBooleanOption('drawGapEdgePoints', e.setName);

                var points = e.points;
                var setName = e.setName;
                var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));

                var stroking = strokePattern && strokePattern.length >= 2;

                var ctx = e.drawingContext;
                ctx.save();
                if (stroking) {
                    if (ctx.setLineDash) ctx.setLineDash(strokePattern);
                }

                var pointsOnLine = DygraphCanvasRenderer._drawSeries(e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color);
                DygraphCanvasRenderer._drawPointsOnLine(e, pointsOnLine, drawPointCallback, color, pointSize);

                if (stroking) {
                    if (ctx.setLineDash) ctx.setLineDash([]);
                }

                ctx.restore();
            };

            /**
             * This does the actual drawing of lines on the canvas, for just one series.
             * Returns a list of [canvasx, canvasy] pairs for points for which a
             * drawPointCallback should be fired.  These include isolated points, or all
             * points if drawPoints=true.
             * @param {Object} e The dictionary passed to the plotter function.
             * @private
             */
            DygraphCanvasRenderer._drawSeries = function (e, iter, strokeWidth, pointSize, drawPoints, drawGapPoints, stepPlot, color) {

                var prevCanvasX = null;
                var prevCanvasY = null;
                var nextCanvasY = null;
                var isIsolated; // true if this point is isolated (no line segments)
                var point; // the point being processed in the while loop
                var pointsOnLine = []; // Array of [canvasx, canvasy] pairs.
                var first = true; // the first cycle through the while loop

                var ctx = e.drawingContext;
                ctx.beginPath();
                ctx.strokeStyle = color;
                ctx.lineWidth = strokeWidth;

                // NOTE: we break the iterator's encapsulation here for about a 25% speedup.
                var arr = iter.array_;
                var limit = iter.end_;
                var predicate = iter.predicate_;

                for (var i = iter.start_; i < limit; i++) {
                    point = arr[i];
                    if (predicate) {
                        while (i < limit && !predicate(arr, i)) {
                            i++;
                        }
                        if (i == limit) break;
                        point = arr[i];
                    }

                    // FIXME: The 'canvasy != canvasy' test here catches NaN values but the test
                    // doesn't catch Infinity values. Could change this to
                    // !isFinite(point.canvasy), but I assume it avoids isNaN for performance?
                    if (point.canvasy === null || point.canvasy != point.canvasy) {
                        if (stepPlot && prevCanvasX !== null) {
                            // Draw a horizontal line to the start of the missing data
                            ctx.moveTo(prevCanvasX, prevCanvasY);
                            ctx.lineTo(point.canvasx, prevCanvasY);
                        }
                        prevCanvasX = prevCanvasY = null;
                    } else {
                        isIsolated = false;
                        if (drawGapPoints || prevCanvasX === null) {
                            iter.nextIdx_ = i;
                            iter.next();
                            nextCanvasY = iter.hasNext ? iter.peek.canvasy : null;

                            var isNextCanvasYNullOrNaN = nextCanvasY === null || nextCanvasY != nextCanvasY;
                            isIsolated = prevCanvasX === null && isNextCanvasYNullOrNaN;
                            if (drawGapPoints) {
                                // Also consider a point to be "isolated" if it's adjacent to a
                                // null point, excluding the graph edges.
                                if (!first && prevCanvasX === null || iter.hasNext && isNextCanvasYNullOrNaN) {
                                    isIsolated = true;
                                }
                            }
                        }

                        if (prevCanvasX !== null) {
                            if (strokeWidth) {
                                if (stepPlot) {
                                    ctx.moveTo(prevCanvasX, prevCanvasY);
                                    ctx.lineTo(point.canvasx, prevCanvasY);
                                }

                                ctx.lineTo(point.canvasx, point.canvasy);
                            }
                        } else {
                            ctx.moveTo(point.canvasx, point.canvasy);
                        }
                        if (drawPoints || isIsolated) {
                            pointsOnLine.push([point.canvasx, point.canvasy, point.idx]);
                        }
                        prevCanvasX = point.canvasx;
                        prevCanvasY = point.canvasy;
                    }
                    first = false;
                }
                ctx.stroke();
                return pointsOnLine;
            };

            /**
             * This fires the drawPointCallback functions, which draw dots on the points by
             * default. This gets used when the "drawPoints" option is set, or when there
             * are isolated points.
             * @param {Object} e The dictionary passed to the plotter function.
             * @private
             */
            DygraphCanvasRenderer._drawPointsOnLine = function (e, pointsOnLine, drawPointCallback, color, pointSize) {
                var ctx = e.drawingContext;
                for (var idx = 0; idx < pointsOnLine.length; idx++) {
                    var cb = pointsOnLine[idx];
                    ctx.save();
                    drawPointCallback.call(e.dygraph, e.dygraph, e.setName, ctx, cb[0], cb[1], color, pointSize, cb[2]);
                    ctx.restore();
                }
            };

            /**
             * Attaches canvas coordinates to the points array.
             * @private
             */
            DygraphCanvasRenderer.prototype._updatePoints = function () {
                // Update Points
                // TODO(danvk): here
                //
                // TODO(bhs): this loop is a hot-spot for high-point-count charts. These
                // transformations can be pushed into the canvas via linear transformation
                // matrices.
                // NOTE(danvk): this is trickier than it sounds at first. The transformation
                // needs to be done before the .moveTo() and .lineTo() calls, but must be
                // undone before the .stroke() call to ensure that the stroke width is
                // unaffected.  An alternative is to reduce the stroke width in the
                // transformed coordinate space, but you can't specify different values for
                // each dimension (as you can with .scale()). The speedup here is ~12%.
                var sets = this.layout.points;
                for (var i = sets.length; i--;) {
                    var points = sets[i];
                    for (var j = points.length; j--;) {
                        var point = points[j];
                        point.canvasx = this.area.w * point.x + this.area.x;
                        point.canvasy = this.area.h * point.y + this.area.y;
                    }
                }
            };

            /**
             * Add canvas Actually draw the lines chart, including error bars.
             *
             * This function can only be called if DygraphLayout's points array has been
             * updated with canvas{x,y} attributes, i.e. by
             * DygraphCanvasRenderer._updatePoints.
             *
             * @param {string=} opt_seriesName when specified, only that series will
             *     be drawn. (This is used for expedited redrawing with highlightSeriesOpts)
             * @param {CanvasRenderingContext2D} opt_ctx when specified, the drawing
             *     context.  However, lines are typically drawn on the object's
             *     elementContext.
             * @private
             */
            DygraphCanvasRenderer.prototype._renderLineChart = function (opt_seriesName, opt_ctx) {
                var ctx = opt_ctx || this.elementContext;
                var i;

                var sets = this.layout.points;
                var setNames = this.layout.setNames;
                var setName;

                this.colors = this.dygraph_.colorsMap_;

                // Determine which series have specialized plotters.
                var plotter_attr = this.dygraph_.getOption("plotter");
                var plotters = plotter_attr;
                if (!utils.isArrayLike(plotters)) {
                    plotters = [plotters];
                }

                var setPlotters = {}; // series name -> plotter fn.
                for (i = 0; i < setNames.length; i++) {
                    setName = setNames[i];
                    var setPlotter = this.dygraph_.getOption("plotter", setName);
                    if (setPlotter == plotter_attr) continue; // not specialized.

                    setPlotters[setName] = setPlotter;
                }

                for (i = 0; i < plotters.length; i++) {
                    var plotter = plotters[i];
                    var is_last = i == plotters.length - 1;

                    for (var j = 0; j < sets.length; j++) {
                        setName = setNames[j];
                        if (opt_seriesName && setName != opt_seriesName) continue;

                        var points = sets[j];

                        // Only throw in the specialized plotters on the last iteration.
                        var p = plotter;
                        if (setName in setPlotters) {
                            if (is_last) {
                                p = setPlotters[setName];
                            } else {
                                // Don't use the standard plotters in this case.
                                continue;
                            }
                        }

                        var color = this.colors[setName];
                        var strokeWidth = this.dygraph_.getOption("strokeWidth", setName);

                        ctx.save();
                        ctx.strokeStyle = color;
                        ctx.lineWidth = strokeWidth;
                        p({
                            points: points,
                            setName: setName,
                            drawingContext: ctx,
                            color: color,
                            strokeWidth: strokeWidth,
                            dygraph: this.dygraph_,
                            axis: this.dygraph_.axisPropertiesForSeries(setName),
                            plotArea: this.area,
                            seriesIndex: j,
                            seriesCount: sets.length,
                            singleSeriesName: opt_seriesName,
                            allSeriesPoints: sets
                        });
                        ctx.restore();
                    }
                }
            };

            /**
             * Standard plotters. These may be used by clients via Dygraph.Plotters.
             * See comments there for more details.
             */
            DygraphCanvasRenderer._Plotters = {
                linePlotter: function linePlotter(e) {
                    DygraphCanvasRenderer._linePlotter(e);
                },

                fillPlotter: function fillPlotter(e) {
                    DygraphCanvasRenderer._fillPlotter(e);
                },

                errorPlotter: function errorPlotter(e) {
                    DygraphCanvasRenderer._errorPlotter(e);
                }
            };

            /**
             * Plotter which draws the central lines for a series.
             * @private
             */
            DygraphCanvasRenderer._linePlotter = function (e) {
                var g = e.dygraph;
                var setName = e.setName;
                var strokeWidth = e.strokeWidth;

                // TODO(danvk): Check if there's any performance impact of just calling
                // getOption() inside of _drawStyledLine. Passing in so many parameters makes
                // this code a bit nasty.
                var borderWidth = g.getNumericOption("strokeBorderWidth", setName);
                var drawPointCallback = g.getOption("drawPointCallback", setName) || utils.Circles.DEFAULT;
                var strokePattern = g.getOption("strokePattern", setName);
                var drawPoints = g.getBooleanOption("drawPoints", setName);
                var pointSize = g.getNumericOption("pointSize", setName);

                if (borderWidth && strokeWidth) {
                    DygraphCanvasRenderer._drawStyledLine(e, g.getOption("strokeBorderColor", setName), strokeWidth + 2 * borderWidth, strokePattern, drawPoints, drawPointCallback, pointSize);
                }

                DygraphCanvasRenderer._drawStyledLine(e, e.color, strokeWidth, strokePattern, drawPoints, drawPointCallback, pointSize);
            };

            /**
             * Draws the shaded error bars/confidence intervals for each series.
             * This happens before the center lines are drawn, since the center lines
             * need to be drawn on top of the error bars for all series.
             * @private
             */
            DygraphCanvasRenderer._errorPlotter = function (e) {
                var g = e.dygraph;
                var setName = e.setName;
                var errorBars = g.getBooleanOption("errorBars") || g.getBooleanOption("customBars");
                if (!errorBars) return;

                var fillGraph = g.getBooleanOption("fillGraph", setName);
                if (fillGraph) {
                    console.warn("Can't use fillGraph option with error bars");
                }

                var ctx = e.drawingContext;
                var color = e.color;
                var fillAlpha = g.getNumericOption('fillAlpha', setName);
                var stepPlot = g.getBooleanOption("stepPlot", setName);
                var points = e.points;

                var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));

                var newYs;

                // setup graphics context
                var prevX = NaN;
                var prevY = NaN;
                var prevYs = [-1, -1];
                // should be same color as the lines but only 15% opaque.
                var rgb = utils.toRGB_(color);
                var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
                ctx.fillStyle = err_color;
                ctx.beginPath();

                var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(x) {
                    return x === null || x === undefined || isNaN(x);
                };

                while (iter.hasNext) {
                    var point = iter.next();
                    if (!stepPlot && isNullUndefinedOrNaN(point.y) || stepPlot && !isNaN(prevY) && isNullUndefinedOrNaN(prevY)) {
                        prevX = NaN;
                        continue;
                    }

                    newYs = [point.y_bottom, point.y_top];
                    if (stepPlot) {
                        prevY = point.y;
                    }

                    // The documentation specifically disallows nulls inside the point arrays,
                    // but in case it happens we should do something sensible.
                    if (isNaN(newYs[0])) newYs[0] = point.y;
                    if (isNaN(newYs[1])) newYs[1] = point.y;

                    newYs[0] = e.plotArea.h * newYs[0] + e.plotArea.y;
                    newYs[1] = e.plotArea.h * newYs[1] + e.plotArea.y;
                    if (!isNaN(prevX)) {
                        if (stepPlot) {
                            ctx.moveTo(prevX, prevYs[0]);
                            ctx.lineTo(point.canvasx, prevYs[0]);
                            ctx.lineTo(point.canvasx, prevYs[1]);
                        } else {
                            ctx.moveTo(prevX, prevYs[0]);
                            ctx.lineTo(point.canvasx, newYs[0]);
                            ctx.lineTo(point.canvasx, newYs[1]);
                        }
                        ctx.lineTo(prevX, prevYs[1]);
                        ctx.closePath();
                    }
                    prevYs = newYs;
                    prevX = point.canvasx;
                }
                ctx.fill();
            };

            /**
             * Proxy for CanvasRenderingContext2D which drops moveTo/lineTo calls which are
             * superfluous. It accumulates all movements which haven't changed the x-value
             * and only applies the two with the most extreme y-values.
             *
             * Calls to lineTo/moveTo must have non-decreasing x-values.
             */
            DygraphCanvasRenderer._fastCanvasProxy = function (context) {
                var pendingActions = []; // array of [type, x, y] tuples
                var lastRoundedX = null;
                var lastFlushedX = null;

                var LINE_TO = 1,
                    MOVE_TO = 2;

                var actionCount = 0; // number of moveTos and lineTos passed to context.

                // Drop superfluous motions
                // Assumes all pendingActions have the same (rounded) x-value.
                var compressActions = function compressActions(opt_losslessOnly) {
                    if (pendingActions.length <= 1) return;

                    // Lossless compression: drop inconsequential moveTos.
                    for (var i = pendingActions.length - 1; i > 0; i--) {
                        var action = pendingActions[i];
                        if (action[0] == MOVE_TO) {
                            var prevAction = pendingActions[i - 1];
                            if (prevAction[1] == action[1] && prevAction[2] == action[2]) {
                                pendingActions.splice(i, 1);
                            }
                        }
                    }

                    // Lossless compression: ... drop consecutive moveTos ...
                    for (var i = 0; i < pendingActions.length - 1;) /* incremented internally */ {
                        var action = pendingActions[i];
                        if (action[0] == MOVE_TO && pendingActions[i + 1][0] == MOVE_TO) {
                            pendingActions.splice(i, 1);
                        } else {
                            i++;
                        }
                    }

                    // Lossy compression: ... drop all but the extreme y-values ...
                    if (pendingActions.length > 2 && !opt_losslessOnly) {
                        // keep an initial moveTo, but drop all others.
                        var startIdx = 0;
                        if (pendingActions[0][0] == MOVE_TO) startIdx++;
                        var minIdx = null,
                            maxIdx = null;
                        for (var i = startIdx; i < pendingActions.length; i++) {
                            var action = pendingActions[i];
                            if (action[0] != LINE_TO) continue;
                            if (minIdx === null && maxIdx === null) {
                                minIdx = i;
                                maxIdx = i;
                            } else {
                                var y = action[2];
                                if (y < pendingActions[minIdx][2]) {
                                    minIdx = i;
                                } else if (y > pendingActions[maxIdx][2]) {
                                    maxIdx = i;
                                }
                            }
                        }
                        var minAction = pendingActions[minIdx],
                            maxAction = pendingActions[maxIdx];
                        pendingActions.splice(startIdx, pendingActions.length - startIdx);
                        if (minIdx < maxIdx) {
                            pendingActions.push(minAction);
                            pendingActions.push(maxAction);
                        } else if (minIdx > maxIdx) {
                            pendingActions.push(maxAction);
                            pendingActions.push(minAction);
                        } else {
                            pendingActions.push(minAction);
                        }
                    }
                };

                var flushActions = function flushActions(opt_noLossyCompression) {
                    compressActions(opt_noLossyCompression);
                    for (var i = 0, len = pendingActions.length; i < len; i++) {
                        var action = pendingActions[i];
                        if (action[0] == LINE_TO) {
                            context.lineTo(action[1], action[2]);
                        } else if (action[0] == MOVE_TO) {
                            context.moveTo(action[1], action[2]);
                        }
                    }
                    if (pendingActions.length) {
                        lastFlushedX = pendingActions[pendingActions.length - 1][1];
                    }
                    actionCount += pendingActions.length;
                    pendingActions = [];
                };

                var addAction = function addAction(action, x, y) {
                    var rx = Math.round(x);
                    if (lastRoundedX === null || rx != lastRoundedX) {
                        // if there are large gaps on the x-axis, it's essential to keep the
                        // first and last point as well.
                        var hasGapOnLeft = lastRoundedX - lastFlushedX > 1,
                            hasGapOnRight = rx - lastRoundedX > 1,
                            hasGap = hasGapOnLeft || hasGapOnRight;
                        flushActions(hasGap);
                        lastRoundedX = rx;
                    }
                    pendingActions.push([action, x, y]);
                };

                return {
                    moveTo: function moveTo(x, y) {
                        addAction(MOVE_TO, x, y);
                    },
                    lineTo: function lineTo(x, y) {
                        addAction(LINE_TO, x, y);
                    },

                    // for major operations like stroke/fill, we skip compression to ensure
                    // that there are no artifacts at the right edge.
                    stroke: function stroke() {
                        flushActions(true); context.stroke();
                    },
                    fill: function fill() {
                        flushActions(true); context.fill();
                    },
                    beginPath: function beginPath() {
                        flushActions(true); context.beginPath();
                    },
                    closePath: function closePath() {
                        flushActions(true); context.closePath();
                    },

                    _count: function _count() {
                        return actionCount;
                    }
                };
            };

            /**
             * Draws the shaded regions when "fillGraph" is set. Not to be confused with
             * error bars.
             *
             * For stacked charts, it's more convenient to handle all the series
             * simultaneously. So this plotter plots all the points on the first series
             * it's asked to draw, then ignores all the other series.
             *
             * @private
             */
            DygraphCanvasRenderer._fillPlotter = function (e) {
                // Skip if we're drawing a single series for interactive highlight overlay.
                if (e.singleSeriesName) return;

                // We'll handle all the series at once, not one-by-one.
                if (e.seriesIndex !== 0) return;

                var g = e.dygraph;
                var setNames = g.getLabels().slice(1); // remove x-axis

                // getLabels() includes names for invisible series, which are not included in
                // allSeriesPoints. We remove those to make the two match.
                // TODO(danvk): provide a simpler way to get this information.
                for (var i = setNames.length; i >= 0; i--) {
                    if (!g.visibility()[i]) setNames.splice(i, 1);
                }

                var anySeriesFilled = (function () {
                    for (var i = 0; i < setNames.length; i++) {
                        if (g.getBooleanOption("fillGraph", setNames[i])) return true;
                    }
                    return false;
                })();

                if (!anySeriesFilled) return;

                var area = e.plotArea;
                var sets = e.allSeriesPoints;
                var setCount = sets.length;

                var stackedGraph = g.getBooleanOption("stackedGraph");
                var colors = g.getColors();

                // For stacked graphs, track the baseline for filling.
                //
                // The filled areas below graph lines are trapezoids with two
                // vertical edges. The top edge is the line segment being drawn, and
                // the baseline is the bottom edge. Each baseline corresponds to the
                // top line segment from the previous stacked line. In the case of
                // step plots, the trapezoids are rectangles.
                var baseline = {};
                var currBaseline;
                var prevStepPlot; // for different line drawing modes (line/step) per series

                // Helper function to trace a line back along the baseline.
                var traceBackPath = function traceBackPath(ctx, baselineX, baselineY, pathBack) {
                    ctx.lineTo(baselineX, baselineY);
                    if (stackedGraph) {
                        for (var i = pathBack.length - 1; i >= 0; i--) {
                            var pt = pathBack[i];
                            ctx.lineTo(pt[0], pt[1]);
                        }
                    }
                };

                // process sets in reverse order (needed for stacked graphs)
                for (var setIdx = setCount - 1; setIdx >= 0; setIdx--) {
                    var ctx = e.drawingContext;
                    var setName = setNames[setIdx];
                    if (!g.getBooleanOption('fillGraph', setName)) continue;

                    var fillAlpha = g.getNumericOption('fillAlpha', setName);
                    var stepPlot = g.getBooleanOption('stepPlot', setName);
                    var color = colors[setIdx];
                    var axis = g.axisPropertiesForSeries(setName);
                    var axisY = 1.0 + axis.minyval * axis.yscale;
                    if (axisY < 0.0) axisY = 0.0; else if (axisY > 1.0) axisY = 1.0;
                    axisY = area.h * axisY + area.y;

                    var points = sets[setIdx];
                    var iter = utils.createIterator(points, 0, points.length, DygraphCanvasRenderer._getIteratorPredicate(g.getBooleanOption("connectSeparatedPoints", setName)));

                    // setup graphics context
                    var prevX = NaN;
                    var prevYs = [-1, -1];
                    var newYs;
                    // should be same color as the lines but only 15% opaque.
                    var rgb = utils.toRGB_(color);
                    var err_color = 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + fillAlpha + ')';
                    ctx.fillStyle = err_color;
                    ctx.beginPath();
                    var last_x,
                        is_first = true;

                    // If the point density is high enough, dropping segments on their way to
                    // the canvas justifies the overhead of doing so.
                    if (points.length > 2 * g.width_ || _dygraph2['default'].FORCE_FAST_PROXY) {
                        ctx = DygraphCanvasRenderer._fastCanvasProxy(ctx);
                    }

                    // For filled charts, we draw points from left to right, then back along
                    // the x-axis to complete a shape for filling.
                    // For stacked plots, this "back path" is a more complex shape. This array
                    // stores the [x, y] values needed to trace that shape.
                    var pathBack = [];

                    // TODO(danvk): there are a lot of options at play in this loop.
                    //     The logic would be much clearer if some (e.g. stackGraph and
                    //     stepPlot) were split off into separate sub-plotters.
                    var point;
                    while (iter.hasNext) {
                        point = iter.next();
                        if (!utils.isOK(point.y) && !stepPlot) {
                            traceBackPath(ctx, prevX, prevYs[1], pathBack);
                            pathBack = [];
                            prevX = NaN;
                            if (point.y_stacked !== null && !isNaN(point.y_stacked)) {
                                baseline[point.canvasx] = area.h * point.y_stacked + area.y;
                            }
                            continue;
                        }
                        if (stackedGraph) {
                            if (!is_first && last_x == point.xval) {
                                continue;
                            } else {
                                is_first = false;
                                last_x = point.xval;
                            }

                            currBaseline = baseline[point.canvasx];
                            var lastY;
                            if (currBaseline === undefined) {
                                lastY = axisY;
                            } else {
                                if (prevStepPlot) {
                                    lastY = currBaseline[0];
                                } else {
                                    lastY = currBaseline;
                                }
                            }
                            newYs = [point.canvasy, lastY];

                            if (stepPlot) {
                                // Step plots must keep track of the top and bottom of
                                // the baseline at each point.
                                if (prevYs[0] === -1) {
                                    baseline[point.canvasx] = [point.canvasy, axisY];
                                } else {
                                    baseline[point.canvasx] = [point.canvasy, prevYs[0]];
                                }
                            } else {
                                baseline[point.canvasx] = point.canvasy;
                            }
                        } else {
                            if (isNaN(point.canvasy) && stepPlot) {
                                newYs = [area.y + area.h, axisY];
                            } else {
                                newYs = [point.canvasy, axisY];
                            }
                        }
                        if (!isNaN(prevX)) {
                            // Move to top fill point
                            if (stepPlot) {
                                ctx.lineTo(point.canvasx, prevYs[0]);
                                ctx.lineTo(point.canvasx, newYs[0]);
                            } else {
                                ctx.lineTo(point.canvasx, newYs[0]);
                            }

                            // Record the baseline for the reverse path.
                            if (stackedGraph) {
                                pathBack.push([prevX, prevYs[1]]);
                                if (prevStepPlot && currBaseline) {
                                    // Draw to the bottom of the baseline
                                    pathBack.push([point.canvasx, currBaseline[1]]);
                                } else {
                                    pathBack.push([point.canvasx, newYs[1]]);
                                }
                            }
                        } else {
                            ctx.moveTo(point.canvasx, newYs[1]);
                            ctx.lineTo(point.canvasx, newYs[0]);
                        }
                        prevYs = newYs;
                        prevX = point.canvasx;
                    }
                    prevStepPlot = stepPlot;
                    if (newYs && point) {
                        traceBackPath(ctx, point.canvasx, newYs[1], pathBack);
                        pathBack = [];
                    }
                    ctx.fill();
                }
            };

            exports['default'] = DygraphCanvasRenderer;
            module.exports = exports['default'];

        }, { "./dygraph": 18, "./dygraph-utils": 17 }], 10: [function (require, module, exports) {
            'use strict';

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

            var _dygraphTickers = require('./dygraph-tickers');

            var DygraphTickers = _interopRequireWildcard(_dygraphTickers);

            var _dygraphInteractionModel = require('./dygraph-interaction-model');

            var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel);

            var _dygraphCanvas = require('./dygraph-canvas');

            var _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas);

            var _dygraphUtils = require('./dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            // Default attribute values.
            var DEFAULT_ATTRS = {
                highlightCircleSize: 3,
                highlightSeriesOpts: null,
                highlightSeriesBackgroundAlpha: 0.5,
                highlightSeriesBackgroundColor: 'rgb(255, 255, 255)',

                labelsSeparateLines: false,
                labelsShowZeroValues: true,
                labelsKMB: false,
                labelsKMG2: false,
                showLabelsOnHighlight: true,

                digitsAfterDecimal: 2,
                maxNumberWidth: 6,
                sigFigs: null,

                strokeWidth: 1.0,
                strokeBorderWidth: 0,
                strokeBorderColor: "white",

                axisTickSize: 3,
                axisLabelFontSize: 14,
                rightGap: 5,

                showRoller: false,
                xValueParser: undefined,

                delimiter: ',',

                sigma: 2.0,
                errorBars: false,
                fractions: false,
                wilsonInterval: true, // only relevant if fractions is true
                customBars: false,
                fillGraph: false,
                fillAlpha: 0.15,
                connectSeparatedPoints: false,

                stackedGraph: false,
                stackedGraphNaNFill: 'all',
                hideOverlayOnMouseOut: true,

                legend: 'onmouseover',
                stepPlot: false,
                xRangePad: 0,
                yRangePad: null,
                drawAxesAtZero: false,

                // Sizes of the various chart labels.
                titleHeight: 28,
                xLabelHeight: 18,
                yLabelWidth: 18,

                axisLineColor: "black",
                axisLineWidth: 0.3,
                gridLineWidth: 0.3,
                axisLabelWidth: 50,
                gridLineColor: "rgb(128,128,128)",

                interactionModel: _dygraphInteractionModel2['default'].defaultModel,
                animatedZooms: false, // (for now)

                // Range selector options
                showRangeSelector: false,
                rangeSelectorHeight: 40,
                rangeSelectorPlotStrokeColor: "#808FAB",
                rangeSelectorPlotFillGradientColor: "white",
                rangeSelectorPlotFillColor: "#A7B1C4",
                rangeSelectorBackgroundStrokeColor: "gray",
                rangeSelectorBackgroundLineWidth: 1,
                rangeSelectorPlotLineWidth: 1.5,
                rangeSelectorForegroundStrokeColor: "black",
                rangeSelectorForegroundLineWidth: 1,
                rangeSelectorAlpha: 0.6,
                showInRangeSelector: null,

                // The ordering here ensures that central lines always appear above any
                // fill bars/error bars.
                plotter: [_dygraphCanvas2['default']._fillPlotter, _dygraphCanvas2['default']._errorPlotter, _dygraphCanvas2['default']._linePlotter],

                plugins: [],

                // per-axis options
                axes: {
                    x: {
                        pixelsPerLabel: 70,
                        axisLabelWidth: 60,
                        axisLabelFormatter: utils.dateAxisLabelFormatter,
                        valueFormatter: utils.dateValueFormatter,
                        drawGrid: true,
                        drawAxis: true,
                        independentTicks: true,
                        ticker: DygraphTickers.dateTicker
                    },
                    y: {
                        axisLabelWidth: 50,
                        pixelsPerLabel: 30,
                        valueFormatter: utils.numberValueFormatter,
                        axisLabelFormatter: utils.numberAxisLabelFormatter,
                        drawGrid: true,
                        drawAxis: true,
                        independentTicks: true,
                        ticker: DygraphTickers.numericTicks
                    },
                    y2: {
                        axisLabelWidth: 50,
                        pixelsPerLabel: 30,
                        valueFormatter: utils.numberValueFormatter,
                        axisLabelFormatter: utils.numberAxisLabelFormatter,
                        drawAxis: true, // only applies when there are two axes of data.
                        drawGrid: false,
                        independentTicks: false,
                        ticker: DygraphTickers.numericTicks
                    }
                }
            };

            exports['default'] = DEFAULT_ATTRS;
            module.exports = exports['default'];

        }, { "./dygraph-canvas": 9, "./dygraph-interaction-model": 12, "./dygraph-tickers": 16, "./dygraph-utils": 17 }], 11: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview A wrapper around the Dygraph class which implements the
             * interface for a GViz (aka Google Visualization API) visualization.
             * It is designed to be a drop-in replacement for Google's AnnotatedTimeline,
             * so the documentation at
             * http://code.google.com/apis/chart/interactive/docs/gallery/annotatedtimeline.html
             * translates over directly.
             *
             * For a full demo, see:
             * - http://dygraphs.com/tests/gviz.html
             * - http://dygraphs.com/tests/annotation-gviz.html
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            var _dygraph = require('./dygraph');

            var _dygraph2 = _interopRequireDefault(_dygraph);

            /**
             * A wrapper around Dygraph that implements the gviz API.
             * @param {!HTMLDivElement} container The DOM object the visualization should
             *     live in.
             * @constructor
             */
            var GVizChart = function GVizChart(container) {
                this.container = container;
            };

            /**
             * @param {GVizDataTable} data
             * @param {Object.<*>} options
             */
            GVizChart.prototype.draw = function (data, options) {
                // Clear out any existing dygraph.
                // TODO(danvk): would it make more sense to simply redraw using the current
                // date_graph object?
                this.container.innerHTML = '';
                if (typeof this.date_graph != 'undefined') {
                    this.date_graph.destroy();
                }

                this.date_graph = new _dygraph2['default'](this.container, data, options);
            };

            /**
             * Google charts compatible setSelection
             * Only row selection is supported, all points in the row will be highlighted
             * @param {Array.<{row:number}>} selection_array array of the selected cells
             * @public
             */
            GVizChart.prototype.setSelection = function (selection_array) {
                var row = false;
                if (selection_array.length) {
                    row = selection_array[0].row;
                }
                this.date_graph.setSelection(row);
            };

            /**
             * Google charts compatible getSelection implementation
             * @return {Array.<{row:number,column:number}>} array of the selected cells
             * @public
             */
            GVizChart.prototype.getSelection = function () {
                var selection = [];

                var row = this.date_graph.getSelection();

                if (row < 0) return selection;

                var points = this.date_graph.layout_.points;
                for (var setIdx = 0; setIdx < points.length; ++setIdx) {
                    selection.push({ row: row, column: setIdx + 1 });
                }

                return selection;
            };

            exports['default'] = GVizChart;
            module.exports = exports['default'];

        }, { "./dygraph": 18 }], 12: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2011 Robert Konigsberg (konigsberg@google.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview The default interaction model for Dygraphs. This is kept out
             * of dygraph.js for better navigability.
             * @author Robert Konigsberg (konigsberg@google.com)
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, "__esModule", {
                value: true
            });

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }

            var _dygraphUtils = require('./dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            /**
             * You can drag this many pixels past the edge of the chart and still have it
             * be considered a zoom. This makes it easier to zoom to the exact edge of the
             * chart, a fairly common operation.
             */
            var DRAG_EDGE_MARGIN = 100;

            /**
             * A collection of functions to facilitate build custom interaction models.
             * @class
             */
            var DygraphInteraction = {};

            /**
             * Checks whether the beginning & ending of an event were close enough that it
             * should be considered a click. If it should, dispatch appropriate events.
             * Returns true if the event was treated as a click.
             *
             * @param {Event} event
             * @param {Dygraph} g
             * @param {Object} context
             */
            DygraphInteraction.maybeTreatMouseOpAsClick = function (event, g, context) {
                context.dragEndX = utils.dragGetX_(event, context);
                context.dragEndY = utils.dragGetY_(event, context);
                var regionWidth = Math.abs(context.dragEndX - context.dragStartX);
                var regionHeight = Math.abs(context.dragEndY - context.dragStartY);

                if (regionWidth < 2 && regionHeight < 2 && g.lastx_ !== undefined && g.lastx_ != -1) {
                    DygraphInteraction.treatMouseOpAsClick(g, event, context);
                }

                context.regionWidth = regionWidth;
                context.regionHeight = regionHeight;
            };

            /**
             * Called in response to an interaction model operation that
             * should start the default panning behavior.
             *
             * It's used in the default callback for "mousedown" operations.
             * Custom interaction model builders can use it to provide the default
             * panning behavior.
             *
             * @param {Event} event the event object which led to the startPan call.
             * @param {Dygraph} g The dygraph on which to act.
             * @param {Object} context The dragging context object (with
             *     dragStartX/dragStartY/etc. properties). This function modifies the
             *     context.
             */
            DygraphInteraction.startPan = function (event, g, context) {
                var i, axis;
                context.isPanning = true;
                var xRange = g.xAxisRange();

                if (g.getOptionForAxis("logscale", "x")) {
                    context.initialLeftmostDate = utils.log10(xRange[0]);
                    context.dateRange = utils.log10(xRange[1]) - utils.log10(xRange[0]);
                } else {
                    context.initialLeftmostDate = xRange[0];
                    context.dateRange = xRange[1] - xRange[0];
                }
                context.xUnitsPerPixel = context.dateRange / (g.plotter_.area.w - 1);

                if (g.getNumericOption("panEdgeFraction")) {
                    var maxXPixelsToDraw = g.width_ * g.getNumericOption("panEdgeFraction");
                    var xExtremes = g.xAxisExtremes(); // I REALLY WANT TO CALL THIS xTremes!

                    var boundedLeftX = g.toDomXCoord(xExtremes[0]) - maxXPixelsToDraw;
                    var boundedRightX = g.toDomXCoord(xExtremes[1]) + maxXPixelsToDraw;

                    var boundedLeftDate = g.toDataXCoord(boundedLeftX);
                    var boundedRightDate = g.toDataXCoord(boundedRightX);
                    context.boundedDates = [boundedLeftDate, boundedRightDate];

                    var boundedValues = [];
                    var maxYPixelsToDraw = g.height_ * g.getNumericOption("panEdgeFraction");

                    for (i = 0; i < g.axes_.length; i++) {
                        axis = g.axes_[i];
                        var yExtremes = axis.extremeRange;

                        var boundedTopY = g.toDomYCoord(yExtremes[0], i) + maxYPixelsToDraw;
                        var boundedBottomY = g.toDomYCoord(yExtremes[1], i) - maxYPixelsToDraw;

                        var boundedTopValue = g.toDataYCoord(boundedTopY, i);
                        var boundedBottomValue = g.toDataYCoord(boundedBottomY, i);

                        boundedValues[i] = [boundedTopValue, boundedBottomValue];
                    }
                    context.boundedValues = boundedValues;
                }

                // Record the range of each y-axis at the start of the drag.
                // If any axis has a valueRange, then we want a 2D pan.
                // We can't store data directly in g.axes_, because it does not belong to us
                // and could change out from under us during a pan (say if there's a data
                // update).
                context.is2DPan = false;
                context.axes = [];
                for (i = 0; i < g.axes_.length; i++) {
                    axis = g.axes_[i];
                    var axis_data = {};
                    var yRange = g.yAxisRange(i);
                    // TODO(konigsberg): These values should be in |context|.
                    // In log scale, initialTopValue, dragValueRange and unitsPerPixel are log scale.
                    var logscale = g.attributes_.getForAxis("logscale", i);
                    if (logscale) {
                        axis_data.initialTopValue = utils.log10(yRange[1]);
                        axis_data.dragValueRange = utils.log10(yRange[1]) - utils.log10(yRange[0]);
                    } else {
                        axis_data.initialTopValue = yRange[1];
                        axis_data.dragValueRange = yRange[1] - yRange[0];
                    }
                    axis_data.unitsPerPixel = axis_data.dragValueRange / (g.plotter_.area.h - 1);
                    context.axes.push(axis_data);

                    // While calculating axes, set 2dpan.
                    if (axis.valueRange) context.is2DPan = true;
                }
            };

            /**
             * Called in response to an interaction model operation that
             * responds to an event that pans the view.
             *
             * It's used in the default callback for "mousemove" operations.
             * Custom interaction model builders can use it to provide the default
             * panning behavior.
             *
             * @param {Event} event the event object which led to the movePan call.
             * @param {Dygraph} g The dygraph on which to act.
             * @param {Object} context The dragging context object (with
             *     dragStartX/dragStartY/etc. properties). This function modifies the
             *     context.
             */
            DygraphInteraction.movePan = function (event, g, context) {
                context.dragEndX = utils.dragGetX_(event, context);
                context.dragEndY = utils.dragGetY_(event, context);

                var minDate = context.initialLeftmostDate - (context.dragEndX - context.dragStartX) * context.xUnitsPerPixel;
                if (context.boundedDates) {
                    minDate = Math.max(minDate, context.boundedDates[0]);
                }
                var maxDate = minDate + context.dateRange;
                if (context.boundedDates) {
                    if (maxDate > context.boundedDates[1]) {
                        // Adjust minDate, and recompute maxDate.
                        minDate = minDate - (maxDate - context.boundedDates[1]);
                        maxDate = minDate + context.dateRange;
                    }
                }

                if (g.getOptionForAxis("logscale", "x")) {
                    g.dateWindow_ = [Math.pow(utils.LOG_SCALE, minDate), Math.pow(utils.LOG_SCALE, maxDate)];
                } else {
                    g.dateWindow_ = [minDate, maxDate];
                }

                // y-axis scaling is automatic unless this is a full 2D pan.
                if (context.is2DPan) {

                    var pixelsDragged = context.dragEndY - context.dragStartY;

                    // Adjust each axis appropriately.
                    for (var i = 0; i < g.axes_.length; i++) {
                        var axis = g.axes_[i];
                        var axis_data = context.axes[i];
                        var unitsDragged = pixelsDragged * axis_data.unitsPerPixel;

                        var boundedValue = context.boundedValues ? context.boundedValues[i] : null;

                        // In log scale, maxValue and minValue are the logs of those values.
                        var maxValue = axis_data.initialTopValue + unitsDragged;
                        if (boundedValue) {
                            maxValue = Math.min(maxValue, boundedValue[1]);
                        }
                        var minValue = maxValue - axis_data.dragValueRange;
                        if (boundedValue) {
                            if (minValue < boundedValue[0]) {
                                // Adjust maxValue, and recompute minValue.
                                maxValue = maxValue - (minValue - boundedValue[0]);
                                minValue = maxValue - axis_data.dragValueRange;
                            }
                        }
                        if (g.attributes_.getForAxis("logscale", i)) {
                            axis.valueRange = [Math.pow(utils.LOG_SCALE, minValue), Math.pow(utils.LOG_SCALE, maxValue)];
                        } else {
                            axis.valueRange = [minValue, maxValue];
                        }
                    }
                }

                g.drawGraph_(false);
            };

            /**
             * Called in response to an interaction model operation that
             * responds to an event that ends panning.
             *
             * It's used in the default callback for "mouseup" operations.
             * Custom interaction model builders can use it to provide the default
             * panning behavior.
             *
             * @param {Event} event the event object which led to the endPan call.
             * @param {Dygraph} g The dygraph on which to act.
             * @param {Object} context The dragging context object (with
             *     dragStartX/dragStartY/etc. properties). This function modifies the
             *     context.
             */
            DygraphInteraction.endPan = DygraphInteraction.maybeTreatMouseOpAsClick;

            /**
             * Called in response to an interaction model operation that
             * responds to an event that starts zooming.
             *
             * It's used in the default callback for "mousedown" operations.
             * Custom interaction model builders can use it to provide the default
             * zooming behavior.
             *
             * @param {Event} event the event object which led to the startZoom call.
             * @param {Dygraph} g The dygraph on which to act.
             * @param {Object} context The dragging context object (with
             *     dragStartX/dragStartY/etc. properties). This function modifies the
             *     context.
             */
            DygraphInteraction.startZoom = function (event, g, context) {
                context.isZooming = true;
                context.zoomMoved = false;
            };

            /**
             * Called in response to an interaction model operation that
             * responds to an event that defines zoom boundaries.
             *
             * It's used in the default callback for "mousemove" operations.
             * Custom interaction model builders can use it to provide the default
             * zooming behavior.
             *
             * @param {Event} event the event object which led to the moveZoom call.
             * @param {Dygraph} g The dygraph on which to act.
             * @param {Object} context The dragging context object (with
             *     dragStartX/dragStartY/etc. properties). This function modifies the
             *     context.
             */
            DygraphInteraction.moveZoom = function (event, g, context) {
                context.zoomMoved = true;
                context.dragEndX = utils.dragGetX_(event, context);
                context.dragEndY = utils.dragGetY_(event, context);

                var xDelta = Math.abs(context.dragStartX - context.dragEndX);
                var yDelta = Math.abs(context.dragStartY - context.dragEndY);

                // drag direction threshold for y axis is twice as large as x axis
                context.dragDirection = xDelta < yDelta / 2 ? utils.VERTICAL : utils.HORIZONTAL;

                g.drawZoomRect_(context.dragDirection, context.dragStartX, context.dragEndX, context.dragStartY, context.dragEndY, context.prevDragDirection, context.prevEndX, context.prevEndY);

                context.prevEndX = context.dragEndX;
                context.prevEndY = context.dragEndY;
                context.prevDragDirection = context.dragDirection;
            };

            /**
             * TODO(danvk): move this logic into dygraph.js
             * @param {Dygraph} g
             * @param {Event} event
             * @param {Object} context
             */
            DygraphInteraction.treatMouseOpAsClick = function (g, event, context) {
                var clickCallback = g.getFunctionOption('clickCallback');
                var pointClickCallback = g.getFunctionOption('pointClickCallback');

                var selectedPoint = null;

                // Find out if the click occurs on a point.
                var closestIdx = -1;
                var closestDistance = Number.MAX_VALUE;

                // check if the click was on a particular point.
                for (var i = 0; i < g.selPoints_.length; i++) {
                    var p = g.selPoints_[i];
                    var distance = Math.pow(p.canvasx - context.dragEndX, 2) + Math.pow(p.canvasy - context.dragEndY, 2);
                    if (!isNaN(distance) && (closestIdx == -1 || distance < closestDistance)) {
                        closestDistance = distance;
                        closestIdx = i;
                    }
                }

                // Allow any click within two pixels of the dot.
                var radius = g.getNumericOption('highlightCircleSize') + 2;
                if (closestDistance <= radius * radius) {
                    selectedPoint = g.selPoints_[closestIdx];
                }

                if (selectedPoint) {
                    var e = {
                        cancelable: true,
                        point: selectedPoint,
                        canvasx: context.dragEndX,
                        canvasy: context.dragEndY
                    };
                    var defaultPrevented = g.cascadeEvents_('pointClick', e);
                    if (defaultPrevented) {
                        // Note: this also prevents click / clickCallback from firing.
                        return;
                    }
                    if (pointClickCallback) {
                        pointClickCallback.call(g, event, selectedPoint);
                    }
                }

                var e = {
                    cancelable: true,
                    xval: g.lastx_, // closest point by x value
                    pts: g.selPoints_,
                    canvasx: context.dragEndX,
                    canvasy: context.dragEndY
                };
                if (!g.cascadeEvents_('click', e)) {
                    if (clickCallback) {
                        // TODO(danvk): pass along more info about the points, e.g. 'x'
                        clickCallback.call(g, event, g.lastx_, g.selPoints_);
                    }
                }
            };

            /**
             * Called in response to an interaction model operation that
             * responds to an event that performs a zoom based on previously defined
             * bounds..
             *
             * It's used in the default callback for "mouseup" operations.
             * Custom interaction model builders can use it to provide the default
             * zooming behavior.
             *
             * @param {Event} event the event object which led to the endZoom call.
             * @param {Dygraph} g The dygraph on which to end the zoom.
             * @param {Object} context The dragging context object (with
             *     dragStartX/dragStartY/etc. properties). This function modifies the
             *     context.
             */
            DygraphInteraction.endZoom = function (event, g, context) {
                g.clearZoomRect_();
                context.isZooming = false;
                DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);

                // The zoom rectangle is visibly clipped to the plot area, so its behavior
                // should be as well.
                // See http://code.google.com/p/dygraphs/issues/detail?id=280
                var plotArea = g.getArea();
                if (context.regionWidth >= 10 && context.dragDirection == utils.HORIZONTAL) {
                    var left = Math.min(context.dragStartX, context.dragEndX),
                        right = Math.max(context.dragStartX, context.dragEndX);
                    left = Math.max(left, plotArea.x);
                    right = Math.min(right, plotArea.x + plotArea.w);
                    if (left < right) {
                        g.doZoomX_(left, right);
                    }
                    context.cancelNextDblclick = true;
                } else if (context.regionHeight >= 10 && context.dragDirection == utils.VERTICAL) {
                    var top = Math.min(context.dragStartY, context.dragEndY),
                        bottom = Math.max(context.dragStartY, context.dragEndY);
                    top = Math.max(top, plotArea.y);
                    bottom = Math.min(bottom, plotArea.y + plotArea.h);
                    if (top < bottom) {
                        g.doZoomY_(top, bottom);
                    }
                    context.cancelNextDblclick = true;
                }
                context.dragStartX = null;
                context.dragStartY = null;
            };

            /**
             * @private
             */
            DygraphInteraction.startTouch = function (event, g, context) {
                event.preventDefault(); // touch browsers are all nice.
                if (event.touches.length > 1) {
                    // If the user ever puts two fingers down, it's not a double tap.
                    context.startTimeForDoubleTapMs = null;
                }

                var touches = [];
                for (var i = 0; i < event.touches.length; i++) {
                    var t = event.touches[i];
                    // we dispense with 'dragGetX_' because all touchBrowsers support pageX
                    touches.push({
                        pageX: t.pageX,
                        pageY: t.pageY,
                        dataX: g.toDataXCoord(t.pageX),
                        dataY: g.toDataYCoord(t.pageY)
                        // identifier: t.identifier
                    });
                }
                context.initialTouches = touches;

                if (touches.length == 1) {
                    // This is just a swipe.
                    context.initialPinchCenter = touches[0];
                    context.touchDirections = { x: true, y: true };
                } else if (touches.length >= 2) {
                    // It's become a pinch!
                    // In case there are 3+ touches, we ignore all but the "first" two.

                    // only screen coordinates can be averaged (data coords could be log scale).
                    context.initialPinchCenter = {
                        pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
                        pageY: 0.5 * (touches[0].pageY + touches[1].pageY),

                        // TODO(danvk): remove
                        dataX: 0.5 * (touches[0].dataX + touches[1].dataX),
                        dataY: 0.5 * (touches[0].dataY + touches[1].dataY)
                    };

                    // Make pinches in a 45-degree swath around either axis 1-dimensional zooms.
                    var initialAngle = 180 / Math.PI * Math.atan2(context.initialPinchCenter.pageY - touches[0].pageY, touches[0].pageX - context.initialPinchCenter.pageX);

                    // use symmetry to get it into the first quadrant.
                    initialAngle = Math.abs(initialAngle);
                    if (initialAngle > 90) initialAngle = 90 - initialAngle;

                    context.touchDirections = {
                        x: initialAngle < 90 - 45 / 2,
                        y: initialAngle > 45 / 2
                    };
                }

                // save the full x & y ranges.
                context.initialRange = {
                    x: g.xAxisRange(),
                    y: g.yAxisRange()
                };
            };

            /**
             * @private
             */
            DygraphInteraction.moveTouch = function (event, g, context) {
                // If the tap moves, then it's definitely not part of a double-tap.
                context.startTimeForDoubleTapMs = null;

                var i,
                    touches = [];
                for (i = 0; i < event.touches.length; i++) {
                    var t = event.touches[i];
                    touches.push({
                        pageX: t.pageX,
                        pageY: t.pageY
                    });
                }
                var initialTouches = context.initialTouches;

                var c_now;

                // old and new centers.
                var c_init = context.initialPinchCenter;
                if (touches.length == 1) {
                    c_now = touches[0];
                } else {
                    c_now = {
                        pageX: 0.5 * (touches[0].pageX + touches[1].pageX),
                        pageY: 0.5 * (touches[0].pageY + touches[1].pageY)
                    };
                }

                // this is the "swipe" component
                // we toss it out for now, but could use it in the future.
                var swipe = {
                    pageX: c_now.pageX - c_init.pageX,
                    pageY: c_now.pageY - c_init.pageY
                };
                var dataWidth = context.initialRange.x[1] - context.initialRange.x[0];
                var dataHeight = context.initialRange.y[0] - context.initialRange.y[1];
                swipe.dataX = swipe.pageX / g.plotter_.area.w * dataWidth;
                swipe.dataY = swipe.pageY / g.plotter_.area.h * dataHeight;
                var xScale, yScale;

                // The residual bits are usually split into scale & rotate bits, but we split
                // them into x-scale and y-scale bits.
                if (touches.length == 1) {
                    xScale = 1.0;
                    yScale = 1.0;
                } else if (touches.length >= 2) {
                    var initHalfWidth = initialTouches[1].pageX - c_init.pageX;
                    xScale = (touches[1].pageX - c_now.pageX) / initHalfWidth;

                    var initHalfHeight = initialTouches[1].pageY - c_init.pageY;
                    yScale = (touches[1].pageY - c_now.pageY) / initHalfHeight;
                }

                // Clip scaling to [1/8, 8] to prevent too much blowup.
                xScale = Math.min(8, Math.max(0.125, xScale));
                yScale = Math.min(8, Math.max(0.125, yScale));

                var didZoom = false;
                if (context.touchDirections.x) {
                    g.dateWindow_ = [c_init.dataX - swipe.dataX + (context.initialRange.x[0] - c_init.dataX) / xScale, c_init.dataX - swipe.dataX + (context.initialRange.x[1] - c_init.dataX) / xScale];
                    didZoom = true;
                }

                if (context.touchDirections.y) {
                    for (i = 0; i < 1 /*g.axes_.length*/; i++) {
                        var axis = g.axes_[i];
                        var logscale = g.attributes_.getForAxis("logscale", i);
                        if (logscale) {
                            // TODO(danvk): implement
                        } else {
                            axis.valueRange = [c_init.dataY - swipe.dataY + (context.initialRange.y[0] - c_init.dataY) / yScale, c_init.dataY - swipe.dataY + (context.initialRange.y[1] - c_init.dataY) / yScale];
                            didZoom = true;
                        }
                    }
                }

                g.drawGraph_(false);

                // We only call zoomCallback on zooms, not pans, to mirror desktop behavior.
                if (didZoom && touches.length > 1 && g.getFunctionOption('zoomCallback')) {
                    var viewWindow = g.xAxisRange();
                    g.getFunctionOption("zoomCallback").call(g, viewWindow[0], viewWindow[1], g.yAxisRanges());
                }
            };

            /**
             * @private
             */
            DygraphInteraction.endTouch = function (event, g, context) {
                if (event.touches.length !== 0) {
                    // this is effectively a "reset"
                    DygraphInteraction.startTouch(event, g, context);
                } else if (event.changedTouches.length == 1) {
                    // Could be part of a "double tap"
                    // The heuristic here is that it's a double-tap if the two touchend events
                    // occur within 500ms and within a 50x50 pixel box.
                    var now = new Date().getTime();
                    var t = event.changedTouches[0];
                    if (context.startTimeForDoubleTapMs && now - context.startTimeForDoubleTapMs < 500 && context.doubleTapX && Math.abs(context.doubleTapX - t.screenX) < 50 && context.doubleTapY && Math.abs(context.doubleTapY - t.screenY) < 50) {
                        g.resetZoom();
                    } else {
                        context.startTimeForDoubleTapMs = now;
                        context.doubleTapX = t.screenX;
                        context.doubleTapY = t.screenY;
                    }
                }
            };

            // Determine the distance from x to [left, right].
            var distanceFromInterval = function distanceFromInterval(x, left, right) {
                if (x < left) {
                    return left - x;
                } else if (x > right) {
                    return x - right;
                } else {
                    return 0;
                }
            };

            /**
             * Returns the number of pixels by which the event happens from the nearest
             * edge of the chart. For events in the interior of the chart, this returns zero.
             */
            var distanceFromChart = function distanceFromChart(event, g) {
                var chartPos = utils.findPos(g.canvas_);
                var box = {
                    left: chartPos.x,
                    right: chartPos.x + g.canvas_.offsetWidth,
                    top: chartPos.y,
                    bottom: chartPos.y + g.canvas_.offsetHeight
                };

                var pt = {
                    x: utils.pageX(event),
                    y: utils.pageY(event)
                };

                var dx = distanceFromInterval(pt.x, box.left, box.right),
                    dy = distanceFromInterval(pt.y, box.top, box.bottom);
                return Math.max(dx, dy);
            };

            /**
             * Default interation model for dygraphs. You can refer to specific elements of
             * this when constructing your own interaction model, e.g.:
             * g.updateOptions( {
             *   interactionModel: {
             *     mousedown: DygraphInteraction.defaultInteractionModel.mousedown
             *   }
             * } );
             */
            DygraphInteraction.defaultModel = {
                // Track the beginning of drag events
                mousedown: function mousedown(event, g, context) {
                    // Right-click should not initiate a zoom.
                    if (event.button && event.button == 2) return;

                    context.initializeMouseDown(event, g, context);

                    if (event.altKey || event.shiftKey) {
                        DygraphInteraction.startPan(event, g, context);
                    } else {
                        DygraphInteraction.startZoom(event, g, context);
                    }

                    // Note: we register mousemove/mouseup on document to allow some leeway for
                    // events to move outside of the chart. Interaction model events get
                    // registered on the canvas, which is too small to allow this.
                    var mousemove = function mousemove(event) {
                        if (context.isZooming) {
                            // When the mouse moves >200px from the chart edge, cancel the zoom.
                            var d = distanceFromChart(event, g);
                            if (d < DRAG_EDGE_MARGIN) {
                                DygraphInteraction.moveZoom(event, g, context);
                            } else {
                                if (context.dragEndX !== null) {
                                    context.dragEndX = null;
                                    context.dragEndY = null;
                                    g.clearZoomRect_();
                                }
                            }
                        } else if (context.isPanning) {
                            DygraphInteraction.movePan(event, g, context);
                        }
                    };
                    var mouseup = function mouseup(event) {
                        if (context.isZooming) {
                            if (context.dragEndX !== null) {
                                DygraphInteraction.endZoom(event, g, context);
                            } else {
                                DygraphInteraction.maybeTreatMouseOpAsClick(event, g, context);
                            }
                        } else if (context.isPanning) {
                            DygraphInteraction.endPan(event, g, context);
                        }

                        utils.removeEvent(document, 'mousemove', mousemove);
                        utils.removeEvent(document, 'mouseup', mouseup);
                        context.destroy();
                    };

                    g.addAndTrackEvent(document, 'mousemove', mousemove);
                    g.addAndTrackEvent(document, 'mouseup', mouseup);
                },
                willDestroyContextMyself: true,

                touchstart: function touchstart(event, g, context) {
                    DygraphInteraction.startTouch(event, g, context);
                },
                touchmove: function touchmove(event, g, context) {
                    DygraphInteraction.moveTouch(event, g, context);
                },
                touchend: function touchend(event, g, context) {
                    DygraphInteraction.endTouch(event, g, context);
                },

                // Disable zooming out if panning.
                dblclick: function dblclick(event, g, context) {
                    if (context.cancelNextDblclick) {
                        context.cancelNextDblclick = false;
                        return;
                    }

                    // Give plugins a chance to grab this event.
                    var e = {
                        canvasx: context.dragEndX,
                        canvasy: context.dragEndY,
                        cancelable: true
                    };
                    if (g.cascadeEvents_('dblclick', e)) {
                        return;
                    }

                    if (event.altKey || event.shiftKey) {
                        return;
                    }
                    g.resetZoom();
                }
            };

            /*
            Dygraph.DEFAULT_ATTRS.interactionModel = DygraphInteraction.defaultModel;
            
            // old ways of accessing these methods/properties
            Dygraph.defaultInteractionModel = DygraphInteraction.defaultModel;
            Dygraph.endZoom = DygraphInteraction.endZoom;
            Dygraph.moveZoom = DygraphInteraction.moveZoom;
            Dygraph.startZoom = DygraphInteraction.startZoom;
            Dygraph.endPan = DygraphInteraction.endPan;
            Dygraph.movePan = DygraphInteraction.movePan;
            Dygraph.startPan = DygraphInteraction.startPan;
            */

            DygraphInteraction.nonInteractiveModel_ = {
                mousedown: function mousedown(event, g, context) {
                    context.initializeMouseDown(event, g, context);
                },
                mouseup: DygraphInteraction.maybeTreatMouseOpAsClick
            };

            // Default interaction model when using the range selector.
            DygraphInteraction.dragIsPanInteractionModel = {
                mousedown: function mousedown(event, g, context) {
                    context.initializeMouseDown(event, g, context);
                    DygraphInteraction.startPan(event, g, context);
                },
                mousemove: function mousemove(event, g, context) {
                    if (context.isPanning) {
                        DygraphInteraction.movePan(event, g, context);
                    }
                },
                mouseup: function mouseup(event, g, context) {
                    if (context.isPanning) {
                        DygraphInteraction.endPan(event, g, context);
                    }
                }
            };

            exports["default"] = DygraphInteraction;
            module.exports = exports["default"];

        }, { "./dygraph-utils": 17 }], 13: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview Based on PlotKitLayout, but modified to meet the needs of
             * dygraphs.
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

            var _dygraphUtils = require('./dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            /**
             * Creates a new DygraphLayout object.
             *
             * This class contains all the data to be charted.
             * It uses data coordinates, but also records the chart range (in data
             * coordinates) and hence is able to calculate percentage positions ('In this
             * view, Point A lies 25% down the x-axis.')
             *
             * Two things that it does not do are:
             * 1. Record pixel coordinates for anything.
             * 2. (oddly) determine anything about the layout of chart elements.
             *
             * The naming is a vestige of Dygraph's original PlotKit roots.
             *
             * @constructor
             */
            var DygraphLayout = function DygraphLayout(dygraph) {
                this.dygraph_ = dygraph;
                /**
                 * Array of points for each series.
                 *
                 * [series index][row index in series] = |Point| structure,
                 * where series index refers to visible series only, and the
                 * point index is for the reduced set of points for the current
                 * zoom region (including one point just outside the window).
                 * All points in the same row index share the same X value.
                 *
                 * @type {Array.<Array.<Dygraph.PointType>>}
                 */
                this.points = [];
                this.setNames = [];
                this.annotations = [];
                this.yAxes_ = null;

                // TODO(danvk): it's odd that xTicks_ and yTicks_ are inputs, but xticks and
                // yticks are outputs. Clean this up.
                this.xTicks_ = null;
                this.yTicks_ = null;
            };

            /**
             * Add points for a single series.
             *
             * @param {string} setname Name of the series.
             * @param {Array.<Dygraph.PointType>} set_xy Points for the series.
             */
            DygraphLayout.prototype.addDataset = function (setname, set_xy) {
                this.points.push(set_xy);
                this.setNames.push(setname);
            };

            /**
             * Returns the box which the chart should be drawn in. This is the canvas's
             * box, less space needed for the axis and chart labels.
             *
             * @return {{x: number, y: number, w: number, h: number}}
             */
            DygraphLayout.prototype.getPlotArea = function () {
                return this.area_;
            };

            // Compute the box which the chart should be drawn in. This is the canvas's
            // box, less space needed for axis, chart labels, and other plug-ins.
            // NOTE: This should only be called by Dygraph.predraw_().
            DygraphLayout.prototype.computePlotArea = function () {
                var area = {
                    // TODO(danvk): per-axis setting.
                    x: 0,
                    y: 0
                };

                area.w = this.dygraph_.width_ - area.x - this.dygraph_.getOption('rightGap');
                area.h = this.dygraph_.height_;

                // Let plugins reserve space.
                var e = {
                    chart_div: this.dygraph_.graphDiv,
                    reserveSpaceLeft: function reserveSpaceLeft(px) {
                        var r = {
                            x: area.x,
                            y: area.y,
                            w: px,
                            h: area.h
                        };
                        area.x += px;
                        area.w -= px;
                        return r;
                    },
                    reserveSpaceRight: function reserveSpaceRight(px) {
                        var r = {
                            x: area.x + area.w - px,
                            y: area.y,
                            w: px,
                            h: area.h
                        };
                        area.w -= px;
                        return r;
                    },
                    reserveSpaceTop: function reserveSpaceTop(px) {
                        var r = {
                            x: area.x,
                            y: area.y,
                            w: area.w,
                            h: px
                        };
                        area.y += px;
                        area.h -= px;
                        return r;
                    },
                    reserveSpaceBottom: function reserveSpaceBottom(px) {
                        var r = {
                            x: area.x,
                            y: area.y + area.h - px,
                            w: area.w,
                            h: px
                        };
                        area.h -= px;
                        return r;
                    },
                    chartRect: function chartRect() {
                        return { x: area.x, y: area.y, w: area.w, h: area.h };
                    }
                };
                this.dygraph_.cascadeEvents_('layout', e);

                this.area_ = area;
            };

            DygraphLayout.prototype.setAnnotations = function (ann) {
                // The Dygraph object's annotations aren't parsed. We parse them here and
                // save a copy. If there is no parser, then the user must be using raw format.
                this.annotations = [];
                var parse = this.dygraph_.getOption('xValueParser') || function (x) {
                    return x;
                };
                for (var i = 0; i < ann.length; i++) {
                    var a = {};
                    if (!ann[i].xval && ann[i].x === undefined) {
                        console.error("Annotations must have an 'x' property");
                        return;
                    }
                    if (ann[i].icon && !(ann[i].hasOwnProperty('width') && ann[i].hasOwnProperty('height'))) {
                        console.error("Must set width and height when setting " + "annotation.icon property");
                        return;
                    }
                    utils.update(a, ann[i]);
                    if (!a.xval) a.xval = parse(a.x);
                    this.annotations.push(a);
                }
            };

            DygraphLayout.prototype.setXTicks = function (xTicks) {
                this.xTicks_ = xTicks;
            };

            // TODO(danvk): add this to the Dygraph object's API or move it into Layout.
            DygraphLayout.prototype.setYAxes = function (yAxes) {
                this.yAxes_ = yAxes;
            };

            DygraphLayout.prototype.evaluate = function () {
                this._xAxis = {};
                this._evaluateLimits();
                this._evaluateLineCharts();
                this._evaluateLineTicks();
                this._evaluateAnnotations();
            };

            DygraphLayout.prototype._evaluateLimits = function () {
                var xlimits = this.dygraph_.xAxisRange();
                this._xAxis.minval = xlimits[0];
                this._xAxis.maxval = xlimits[1];
                var xrange = xlimits[1] - xlimits[0];
                this._xAxis.scale = xrange !== 0 ? 1 / xrange : 1.0;

                if (this.dygraph_.getOptionForAxis("logscale", 'x')) {
                    this._xAxis.xlogrange = utils.log10(this._xAxis.maxval) - utils.log10(this._xAxis.minval);
                    this._xAxis.xlogscale = this._xAxis.xlogrange !== 0 ? 1.0 / this._xAxis.xlogrange : 1.0;
                }
                for (var i = 0; i < this.yAxes_.length; i++) {
                    var axis = this.yAxes_[i];
                    axis.minyval = axis.computedValueRange[0];
                    axis.maxyval = axis.computedValueRange[1];
                    axis.yrange = axis.maxyval - axis.minyval;
                    axis.yscale = axis.yrange !== 0 ? 1.0 / axis.yrange : 1.0;

                    if (this.dygraph_.getOption("logscale")) {
                        axis.ylogrange = utils.log10(axis.maxyval) - utils.log10(axis.minyval);
                        axis.ylogscale = axis.ylogrange !== 0 ? 1.0 / axis.ylogrange : 1.0;
                        if (!isFinite(axis.ylogrange) || isNaN(axis.ylogrange)) {
                            console.error('axis ' + i + ' of graph at ' + axis.g + ' can\'t be displayed in log scale for range [' + axis.minyval + ' - ' + axis.maxyval + ']');
                        }
                    }
                }
            };

            DygraphLayout.calcXNormal_ = function (value, xAxis, logscale) {
                if (logscale) {
                    return (utils.log10(value) - utils.log10(xAxis.minval)) * xAxis.xlogscale;
                } else {
                    return (value - xAxis.minval) * xAxis.scale;
                }
            };

            /**
             * @param {DygraphAxisType} axis
             * @param {number} value
             * @param {boolean} logscale
             * @return {number}
             */
            DygraphLayout.calcYNormal_ = function (axis, value, logscale) {
                if (logscale) {
                    var x = 1.0 - (utils.log10(value) - utils.log10(axis.minyval)) * axis.ylogscale;
                    return isFinite(x) ? x : NaN; // shim for v8 issue; see pull request 276
                } else {
                    return 1.0 - (value - axis.minyval) * axis.yscale;
                }
            };

            DygraphLayout.prototype._evaluateLineCharts = function () {
                var isStacked = this.dygraph_.getOption("stackedGraph");
                var isLogscaleForX = this.dygraph_.getOptionForAxis("logscale", 'x');

                for (var setIdx = 0; setIdx < this.points.length; setIdx++) {
                    var points = this.points[setIdx];
                    var setName = this.setNames[setIdx];
                    var connectSeparated = this.dygraph_.getOption('connectSeparatedPoints', setName);
                    var axis = this.dygraph_.axisPropertiesForSeries(setName);
                    // TODO (konigsberg): use optionsForAxis instead.
                    var logscale = this.dygraph_.attributes_.getForSeries("logscale", setName);

                    for (var j = 0; j < points.length; j++) {
                        var point = points[j];

                        // Range from 0-1 where 0 represents left and 1 represents right.
                        point.x = DygraphLayout.calcXNormal_(point.xval, this._xAxis, isLogscaleForX);
                        // Range from 0-1 where 0 represents top and 1 represents bottom
                        var yval = point.yval;
                        if (isStacked) {
                            point.y_stacked = DygraphLayout.calcYNormal_(axis, point.yval_stacked, logscale);
                            if (yval !== null && !isNaN(yval)) {
                                yval = point.yval_stacked;
                            }
                        }
                        if (yval === null) {
                            yval = NaN;
                            if (!connectSeparated) {
                                point.yval = NaN;
                            }
                        }
                        point.y = DygraphLayout.calcYNormal_(axis, yval, logscale);
                    }

                    this.dygraph_.dataHandler_.onLineEvaluated(points, axis, logscale);
                }
            };

            DygraphLayout.prototype._evaluateLineTicks = function () {
                var i, tick, label, pos, v, has_tick;
                this.xticks = [];
                for (i = 0; i < this.xTicks_.length; i++) {
                    tick = this.xTicks_[i];
                    label = tick.label;
                    has_tick = !('label_v' in tick);
                    v = has_tick ? tick.v : tick.label_v;
                    pos = this.dygraph_.toPercentXCoord(v);
                    if (pos >= 0.0 && pos < 1.0) {
                        this.xticks.push({ pos: pos, label: label, has_tick: has_tick });
                    }
                }

                this.yticks = [];
                for (i = 0; i < this.yAxes_.length; i++) {
                    var axis = this.yAxes_[i];
                    for (var j = 0; j < axis.ticks.length; j++) {
                        tick = axis.ticks[j];
                        label = tick.label;
                        has_tick = !('label_v' in tick);
                        v = has_tick ? tick.v : tick.label_v;
                        pos = this.dygraph_.toPercentYCoord(v, i);
                        if (pos > 0.0 && pos <= 1.0) {
                            this.yticks.push({ axis: i, pos: pos, label: label, has_tick: has_tick });
                        }
                    }
                }
            };

            DygraphLayout.prototype._evaluateAnnotations = function () {
                // Add the annotations to the point to which they belong.
                // Make a map from (setName, xval) to annotation for quick lookups.
                var i;
                var annotations = {};
                for (i = 0; i < this.annotations.length; i++) {
                    var a = this.annotations[i];
                    annotations[a.xval + "," + a.series] = a;
                }

                this.annotated_points = [];

                // Exit the function early if there are no annotations.
                if (!this.annotations || !this.annotations.length) {
                    return;
                }

                // TODO(antrob): loop through annotations not points.
                for (var setIdx = 0; setIdx < this.points.length; setIdx++) {
                    var points = this.points[setIdx];
                    for (i = 0; i < points.length; i++) {
                        var p = points[i];
                        var k = p.xval + "," + p.name;
                        if (k in annotations) {
                            p.annotation = annotations[k];
                            this.annotated_points.push(p);
                        }
                    }
                }
            };

            /**
             * Convenience function to remove all the data sets from a graph
             */
            DygraphLayout.prototype.removeAllDatasets = function () {
                delete this.points;
                delete this.setNames;
                delete this.setPointsLengths;
                delete this.setPointsOffsets;
                this.points = [];
                this.setNames = [];
                this.setPointsLengths = [];
                this.setPointsOffsets = [];
            };

            exports['default'] = DygraphLayout;
            module.exports = exports['default'];

        }, { "./dygraph-utils": 17 }], 14: [function (require, module, exports) {
            (function (process) {
                /**
                 * @license
                 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
                 * MIT-licensed (http://opensource.org/licenses/MIT)
                 */

                "use strict";

                Object.defineProperty(exports, '__esModule', {
                    value: true
                });
                var OPTIONS_REFERENCE = null;

                // For "production" code, this gets removed by uglifyjs.
                if (typeof process !== 'undefined') {
                    if ("development" != 'production') {

                        // NOTE: in addition to parsing as JS, this snippet is expected to be valid
                        // JSON. This assumption cannot be checked in JS, but it will be checked when
                        // documentation is generated by the generate-documentation.py script. For the
                        // most part, this just means that you should always use double quotes.
                        OPTIONS_REFERENCE = // <JSON>
                            {
                                "xValueParser": {
                                    "default": "parseFloat() or Date.parse()*",
                                    "labels": ["CSV parsing"],
                                    "type": "function(str) -> number",
                                    "description": "A function which parses x-values (i.e. the dependent series). Must return a number, even when the values are dates. In this case, millis since epoch are used. This is used primarily for parsing CSV data. *=Dygraphs is slightly more accepting in the dates which it will parse. See code for details."
                                },
                                "stackedGraph": {
                                    "default": "false",
                                    "labels": ["Data Line display"],
                                    "type": "boolean",
                                    "description": "If set, stack series on top of one another rather than drawing them independently. The first series specified in the input data will wind up on top of the chart and the last will be on bottom. NaN values are drawn as white areas without a line on top, see stackedGraphNaNFill for details."
                                },
                                "stackedGraphNaNFill": {
                                    "default": "all",
                                    "labels": ["Data Line display"],
                                    "type": "string",
                                    "description": "Controls handling of NaN values inside a stacked graph. NaN values are interpolated/extended for stacking purposes, but the actual point value remains NaN in the legend display. Valid option values are \"all\" (interpolate internally, repeat leftmost and rightmost value as needed), \"inside\" (interpolate internally only, use zero outside leftmost and rightmost value), and \"none\" (treat NaN as zero everywhere)."
                                },
                                "pointSize": {
                                    "default": "1",
                                    "labels": ["Data Line display"],
                                    "type": "integer",
                                    "description": "The size of the dot to draw on each point in pixels (see drawPoints). A dot is always drawn when a point is \"isolated\", i.e. there is a missing point on either side of it. This also controls the size of those dots."
                                },
                                "drawPoints": {
                                    "default": "false",
                                    "labels": ["Data Line display"],
                                    "type": "boolean",
                                    "description": "Draw a small dot at each point, in addition to a line going through the point. This makes the individual data points easier to see, but can increase visual clutter in the chart. The small dot can be replaced with a custom rendering by supplying a <a href='#drawPointCallback'>drawPointCallback</a>."
                                },
                                "drawGapEdgePoints": {
                                    "default": "false",
                                    "labels": ["Data Line display"],
                                    "type": "boolean",
                                    "description": "Draw points at the edges of gaps in the data. This improves visibility of small data segments or other data irregularities."
                                },
                                "drawPointCallback": {
                                    "default": "null",
                                    "labels": ["Data Line display"],
                                    "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)",
                                    "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]],
                                    "description": "Draw a custom item when drawPoints is enabled. Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy).  Also see <a href='#drawHighlightPointCallback'>drawHighlightPointCallback</a>"
                                },
                                "height": {
                                    "default": "320",
                                    "labels": ["Overall display"],
                                    "type": "integer",
                                    "description": "Height, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
                                },
                                "zoomCallback": {
                                    "default": "null",
                                    "labels": ["Callbacks"],
                                    "type": "function(minDate, maxDate, yRanges)",
                                    "parameters": [["minDate", "milliseconds since epoch"], ["maxDate", "milliseconds since epoch."], ["yRanges", "is an array of [bottom, top] pairs, one for each y-axis."]],
                                    "description": "A function to call when the zoom window is changed (either by zooming in or out). When animatedZooms is set, zoomCallback is called once at the end of the transition (it will not be called for intermediate frames)."
                                },
                                "pointClickCallback": {
                                    "snippet": "function(e, point){<br>&nbsp;&nbsp;alert(point);<br>}",
                                    "default": "null",
                                    "labels": ["Callbacks", "Interactive Elements"],
                                    "type": "function(e, point)",
                                    "parameters": [["e", "the event object for the click"], ["point", "the point that was clicked See <a href='#point_properties'>Point properties</a> for details"]],
                                    "description": "A function to call when a data point is clicked. and the point that was clicked."
                                },
                                "color": {
                                    "default": "(see description)",
                                    "labels": ["Data Series Colors"],
                                    "type": "string",
                                    "example": "red",
                                    "description": "A per-series color definition. Used in conjunction with, and overrides, the colors option."
                                },
                                "colors": {
                                    "default": "(see description)",
                                    "labels": ["Data Series Colors"],
                                    "type": "array<string>",
                                    "example": "['red', '#00FF00']",
                                    "description": "List of colors for the data series. These can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\", etc. If not specified, equally-spaced points around a color wheel are used. Overridden by the 'color' option."
                                },
                                "connectSeparatedPoints": {
                                    "default": "false",
                                    "labels": ["Data Line display"],
                                    "type": "boolean",
                                    "description": "Usually, when Dygraphs encounters a missing value in a data series, it interprets this as a gap and draws it as such. If, instead, the missing values represents an x-value for which only a different series has data, then you'll want to connect the dots by setting this to true. To explicitly include a gap with this option set, use a value of NaN."
                                },
                                "highlightCallback": {
                                    "default": "null",
                                    "labels": ["Callbacks"],
                                    "type": "function(event, x, points, row, seriesName)",
                                    "description": "When set, this callback gets called every time a new point is highlighted.",
                                    "parameters": [["event", "the JavaScript mousemove event"], ["x", "the x-coordinate of the highlighted points"], ["points", "an array of highlighted points: <code>[ {name: 'series', yval: y-value}, &hellip; ]</code>"], ["row", "integer index of the highlighted row in the data table, starting from 0"], ["seriesName", "name of the highlighted series, only present if highlightSeriesOpts is set."]]
                                },
                                "drawHighlightPointCallback": {
                                    "default": "null",
                                    "labels": ["Data Line display"],
                                    "type": "function(g, seriesName, canvasContext, cx, cy, color, pointSize)",
                                    "parameters": [["g", "the reference graph"], ["seriesName", "the name of the series"], ["canvasContext", "the canvas to draw on"], ["cx", "center x coordinate"], ["cy", "center y coordinate"], ["color", "series color"], ["pointSize", "the radius of the image."], ["idx", "the row-index of the point in the data."]],
                                    "description": "Draw a custom item when a point is highlighted.  Default is a small dot matching the series color. This method should constrain drawing to within pointSize pixels from (cx, cy) Also see <a href='#drawPointCallback'>drawPointCallback</a>"
                                },
                                "highlightSeriesOpts": {
                                    "default": "null",
                                    "labels": ["Interactive Elements"],
                                    "type": "Object",
                                    "description": "When set, the options from this object are applied to the timeseries closest to the mouse pointer for interactive highlighting. See also 'highlightCallback'. Example: highlightSeriesOpts: { strokeWidth: 3 }."
                                },
                                "highlightSeriesBackgroundAlpha": {
                                    "default": "0.5",
                                    "labels": ["Interactive Elements"],
                                    "type": "float",
                                    "description": "Fade the background while highlighting series. 1=fully visible background (disable fading), 0=hiddden background (show highlighted series only)."
                                },
                                "highlightSeriesBackgroundColor": {
                                    "default": "rgb(255, 255, 255)",
                                    "labels": ["Interactive Elements"],
                                    "type": "string",
                                    "description": "Sets the background color used to fade out the series in conjunction with 'highlightSeriesBackgroundAlpha'."
                                },
                                "includeZero": {
                                    "default": "false",
                                    "labels": ["Axis display"],
                                    "type": "boolean",
                                    "description": "Usually, dygraphs will use the range of the data plus some padding to set the range of the y-axis. If this option is set, the y-axis will always include zero, typically as the lowest value. This can be used to avoid exaggerating the variance in the data"
                                },
                                "rollPeriod": {
                                    "default": "1",
                                    "labels": ["Error Bars", "Rolling Averages"],
                                    "type": "integer &gt;= 1",
                                    "description": "Number of days over which to average data. Discussed extensively above."
                                },
                                "unhighlightCallback": {
                                    "default": "null",
                                    "labels": ["Callbacks"],
                                    "type": "function(event)",
                                    "parameters": [["event", "the mouse event"]],
                                    "description": "When set, this callback gets called every time the user stops highlighting any point by mousing out of the graph."
                                },
                                "axisTickSize": {
                                    "default": "3.0",
                                    "labels": ["Axis display"],
                                    "type": "number",
                                    "description": "The size of the line to display next to each tick mark on x- or y-axes."
                                },
                                "labelsSeparateLines": {
                                    "default": "false",
                                    "labels": ["Legend"],
                                    "type": "boolean",
                                    "description": "Put <code>&lt;br/&gt;</code> between lines in the label string. Often used in conjunction with <strong>labelsDiv</strong>."
                                },
                                "valueFormatter": {
                                    "default": "Depends on the type of your data.",
                                    "labels": ["Legend", "Value display/formatting"],
                                    "type": "function(num or millis, opts, seriesName, dygraph, row, col)",
                                    "description": "Function to provide a custom display format for the values displayed on mouseover. This does not affect the values that appear on tick marks next to the axes. To format those, see axisLabelFormatter. This is usually set on a <a href='per-axis.html'>per-axis</a> basis. .",
                                    "parameters": [["num_or_millis", "The value to be formatted. This is always a number. For date axes, it's millis since epoch. You can call new Date(millis) to get a Date object."], ["opts", "This is a function you can call to access various options (e.g. opts('labelsKMB')). It returns per-axis values for the option when available."], ["seriesName", "The name of the series from which the point came, e.g. 'X', 'Y', 'A', etc."], ["dygraph", "The dygraph object for which the formatting is being done"], ["row", "The row of the data from which this point comes. g.getValue(row, 0) will return the x-value for this point."], ["col", "The column of the data from which this point comes. g.getValue(row, col) will return the original y-value for this point. This can be used to get the full confidence interval for the point, or access un-rolled values for the point."]]
                                },
                                "annotationMouseOverHandler": {
                                    "default": "null",
                                    "labels": ["Annotations"],
                                    "type": "function(annotation, point, dygraph, event)",
                                    "description": "If provided, this function is called whenever the user mouses over an annotation."
                                },
                                "annotationMouseOutHandler": {
                                    "default": "null",
                                    "labels": ["Annotations"],
                                    "type": "function(annotation, point, dygraph, event)",
                                    "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]],
                                    "description": "If provided, this function is called whenever the user mouses out of an annotation."
                                },
                                "annotationClickHandler": {
                                    "default": "null",
                                    "labels": ["Annotations"],
                                    "type": "function(annotation, point, dygraph, event)",
                                    "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]],
                                    "description": "If provided, this function is called whenever the user clicks on an annotation."
                                },
                                "annotationDblClickHandler": {
                                    "default": "null",
                                    "labels": ["Annotations"],
                                    "type": "function(annotation, point, dygraph, event)",
                                    "parameters": [["annotation", "the annotation left"], ["point", "the point associated with the annotation"], ["dygraph", "the reference graph"], ["event", "the mouse event"]],
                                    "description": "If provided, this function is called whenever the user double-clicks on an annotation."
                                },
                                "drawCallback": {
                                    "default": "null",
                                    "labels": ["Callbacks"],
                                    "type": "function(dygraph, is_initial)",
                                    "parameters": [["dygraph", "The graph being drawn"], ["is_initial", "True if this is the initial draw, false for subsequent draws."]],
                                    "description": "When set, this callback gets called every time the dygraph is drawn. This includes the initial draw, after zooming and repeatedly while panning."
                                },
                                "labelsKMG2": {
                                    "default": "false",
                                    "labels": ["Value display/formatting"],
                                    "type": "boolean",
                                    "description": "Show k/M/G for kilo/Mega/Giga on y-axis. This is different than <code>labelsKMB</code> in that it uses base 2, not 10."
                                },
                                "delimiter": {
                                    "default": ",",
                                    "labels": ["CSV parsing"],
                                    "type": "string",
                                    "description": "The delimiter to look for when separating fields of a CSV file. Setting this to a tab is not usually necessary, since tab-delimited data is auto-detected."
                                },
                                "axisLabelFontSize": {
                                    "default": "14",
                                    "labels": ["Axis display"],
                                    "type": "integer",
                                    "description": "Size of the font (in pixels) to use in the axis labels, both x- and y-axis."
                                },
                                "underlayCallback": {
                                    "default": "null",
                                    "labels": ["Callbacks"],
                                    "type": "function(context, area, dygraph)",
                                    "parameters": [["context", "the canvas drawing context on which to draw"], ["area", "An object with {x,y,w,h} properties describing the drawing area."], ["dygraph", "the reference graph"]],
                                    "description": "When set, this callback gets called before the chart is drawn. It details on how to use this."
                                },
                                "width": {
                                    "default": "480",
                                    "labels": ["Overall display"],
                                    "type": "integer",
                                    "description": "Width, in pixels, of the chart. If the container div has been explicitly sized, this will be ignored."
                                },
                                "pixelRatio": {
                                    "default": "(devicePixelRatio / context.backingStoreRatio)",
                                    "labels": ["Overall display"],
                                    "type": "float",
                                    "description": "Overrides the pixel ratio scaling factor for the canvas's 2d context. Ordinarily, this is set to the devicePixelRatio / (context.backingStoreRatio || 1), so on mobile devices, where the devicePixelRatio can be somewhere around 3, performance can be improved by overriding this value to something less precise, like 1, at the expense of resolution."
                                },
                                "interactionModel": {
                                    "default": "...",
                                    "labels": ["Interactive Elements"],
                                    "type": "Object",
                                    "description": "TODO(konigsberg): document this"
                                },
                                "ticker": {
                                    "default": "Dygraph.dateTicker or Dygraph.numericTicks",
                                    "labels": ["Axis display"],
                                    "type": "function(min, max, pixels, opts, dygraph, vals) -> [{v: ..., label: ...}, ...]",
                                    "parameters": [["min", ""], ["max", ""], ["pixels", ""], ["opts", ""], ["dygraph", "the reference graph"], ["vals", ""]],
                                    "description": "This lets you specify an arbitrary function to generate tick marks on an axis. The tick marks are an array of (value, label) pairs. The built-in functions go to great lengths to choose good tick marks so, if you set this option, you'll most likely want to call one of them and modify the result. See dygraph-tickers.js for an extensive discussion. This is set on a <a href='per-axis.html'>per-axis</a> basis."
                                },
                                "xAxisHeight": {
                                    "default": "(null)",
                                    "labels": ["Axis display"],
                                    "type": "integer",
                                    "description": "Height, in pixels, of the x-axis. If not set explicitly, this is computed based on axisLabelFontSize and axisTickSize."
                                },
                                "showLabelsOnHighlight": {
                                    "default": "true",
                                    "labels": ["Interactive Elements", "Legend"],
                                    "type": "boolean",
                                    "description": "Whether to show the legend upon mouseover."
                                },
                                "axis": {
                                    "default": "(none)",
                                    "labels": ["Axis display"],
                                    "type": "string",
                                    "description": "Set to either 'y1' or 'y2' to assign a series to a y-axis (primary or secondary). Must be set per-series."
                                },
                                "pixelsPerLabel": {
                                    "default": "70 (x-axis) or 30 (y-axes)",
                                    "labels": ["Axis display", "Grid"],
                                    "type": "integer",
                                    "description": "Number of pixels to require between each x- and y-label. Larger values will yield a sparser axis with fewer ticks. This is set on a <a href='per-axis.html'>per-axis</a> basis."
                                },
                                "labelsDiv": {
                                    "default": "null",
                                    "labels": ["Legend"],
                                    "type": "DOM element or string",
                                    "example": "<code style='font-size: small'>document.getElementById('foo')</code>or<code>'foo'",
                                    "description": "Show data labels in an external div, rather than on the graph.  This value can either be a div element or a div id."
                                },
                                "fractions": {
                                    "default": "false",
                                    "labels": ["CSV parsing", "Error Bars"],
                                    "type": "boolean",
                                    "description": "When set, attempt to parse each cell in the CSV file as \"a/b\", where a and b are integers. The ratio will be plotted. This allows computation of Wilson confidence intervals (see below)."
                                },
                                "logscale": {
                                    "default": "false",
                                    "labels": ["Axis display"],
                                    "type": "boolean",
                                    "description": "When set for the y-axis or x-axis, the graph shows that axis in log scale. Any values less than or equal to zero are not displayed. Showing log scale with ranges that go below zero will result in an unviewable graph.\n\n Not compatible with showZero. connectSeparatedPoints is ignored. This is ignored for date-based x-axes."
                                },
                                "strokeWidth": {
                                    "default": "1.0",
                                    "labels": ["Data Line display"],
                                    "type": "float",
                                    "example": "0.5, 2.0",
                                    "description": "The width of the lines connecting data points. This can be used to increase the contrast or some graphs."
                                },
                                "strokePattern": {
                                    "default": "null",
                                    "labels": ["Data Line display"],
                                    "type": "array<integer>",
                                    "example": "[10, 2, 5, 2]",
                                    "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed lines."
                                },
                                "strokeBorderWidth": {
                                    "default": "null",
                                    "labels": ["Data Line display"],
                                    "type": "float",
                                    "example": "1.0",
                                    "description": "Draw a border around graph lines to make crossing lines more easily distinguishable. Useful for graphs with many lines."
                                },
                                "strokeBorderColor": {
                                    "default": "white",
                                    "labels": ["Data Line display"],
                                    "type": "string",
                                    "example": "red, #ccffdd",
                                    "description": "Color for the line border used if strokeBorderWidth is set."
                                },
                                "wilsonInterval": {
                                    "default": "true",
                                    "labels": ["Error Bars"],
                                    "type": "boolean",
                                    "description": "Use in conjunction with the \"fractions\" option. Instead of plotting +/- N standard deviations, dygraphs will compute a Wilson confidence interval and plot that. This has more reasonable behavior for ratios close to 0 or 1."
                                },
                                "fillGraph": {
                                    "default": "false",
                                    "labels": ["Data Line display"],
                                    "type": "boolean",
                                    "description": "Should the area underneath the graph be filled? This option is not compatible with error bars. This may be set on a <a href='per-axis.html'>per-series</a> basis."
                                },
                                "highlightCircleSize": {
                                    "default": "3",
                                    "labels": ["Interactive Elements"],
                                    "type": "integer",
                                    "description": "The size in pixels of the dot drawn over highlighted points."
                                },
                                "gridLineColor": {
                                    "default": "rgb(128,128,128)",
                                    "labels": ["Grid"],
                                    "type": "red, blue",
                                    "description": "The color of the gridlines. This may be set on a per-axis basis to define each axis' grid separately."
                                },
                                "gridLinePattern": {
                                    "default": "null",
                                    "labels": ["Grid"],
                                    "type": "array<integer>",
                                    "example": "[10, 2, 5, 2]",
                                    "description": "A custom pattern array where the even index is a draw and odd is a space in pixels. If null then it draws a solid line. The array should have a even length as any odd lengthed array could be expressed as a smaller even length array. This is used to create dashed gridlines."
                                },
                                "visibility": {
                                    "default": "[true, true, ...]",
                                    "labels": ["Data Line display"],
                                    "type": "Array of booleans",
                                    "description": "Which series should initially be visible? Once the Dygraph has been constructed, you can access and modify the visibility of each series using the <code>visibility</code> and <code>setVisibility</code> methods."
                                },
                                "valueRange": {
                                    "default": "Full range of the input is shown",
                                    "labels": ["Axis display"],
                                    "type": "Array of two numbers",
                                    "example": "[10, 110]",
                                    "description": "Explicitly set the vertical range of the graph to [low, high]. This may be set on a per-axis basis to define each y-axis separately. If either limit is unspecified, it will be calculated automatically (e.g. [null, 30] to automatically calculate just the lower bound)"
                                },
                                "colorSaturation": {
                                    "default": "1.0",
                                    "labels": ["Data Series Colors"],
                                    "type": "float (0.0 - 1.0)",
                                    "description": "If <strong>colors</strong> is not specified, saturation of the automatically-generated data series colors."
                                },
                                "hideOverlayOnMouseOut": {
                                    "default": "true",
                                    "labels": ["Interactive Elements", "Legend"],
                                    "type": "boolean",
                                    "description": "Whether to hide the legend when the mouse leaves the chart area."
                                },
                                "legend": {
                                    "default": "onmouseover",
                                    "labels": ["Legend"],
                                    "type": "string",
                                    "description": "When to display the legend. By default, it only appears when a user mouses over the chart. Set it to \"always\" to always display a legend of some sort. When set to \"follow\", legend follows highlighted points."
                                },
                                "legendFormatter": {
                                    "default": "null",
                                    "labels": ["Legend"],
                                    "type": "function(data): string",
                                    "params": [["data", "An object containing information about the selection (or lack of a selection). This includes formatted values and series information. See <a href=\"https://github.com/danvk/dygraphs/pull/683\">here</a> for sample values."]],
                                    "description": "Set this to supply a custom formatter for the legend. See <a href=\"https://github.com/danvk/dygraphs/pull/683\">this comment</a> and the <a href=\"tests/legend-formatter.html\">legendFormatter demo</a> for usage."
                                },
                                "labelsShowZeroValues": {
                                    "default": "true",
                                    "labels": ["Legend"],
                                    "type": "boolean",
                                    "description": "Show zero value labels in the labelsDiv."
                                },
                                "stepPlot": {
                                    "default": "false",
                                    "labels": ["Data Line display"],
                                    "type": "boolean",
                                    "description": "When set, display the graph as a step plot instead of a line plot. This option may either be set for the whole graph or for single series."
                                },
                                "labelsUTC": {
                                    "default": "false",
                                    "labels": ["Value display/formatting", "Axis display"],
                                    "type": "boolean",
                                    "description": "Show date/time labels according to UTC (instead of local time)."
                                },
                                "labelsKMB": {
                                    "default": "false",
                                    "labels": ["Value display/formatting"],
                                    "type": "boolean",
                                    "description": "Show K/M/B for thousands/millions/billions on y-axis."
                                },
                                "rightGap": {
                                    "default": "5",
                                    "labels": ["Overall display"],
                                    "type": "integer",
                                    "description": "Number of pixels to leave blank at the right edge of the Dygraph. This makes it easier to highlight the right-most data point."
                                },
                                "drawAxesAtZero": {
                                    "default": "false",
                                    "labels": ["Axis display"],
                                    "type": "boolean",
                                    "description": "When set, draw the X axis at the Y=0 position and the Y axis at the X=0 position if those positions are inside the graph's visible area. Otherwise, draw the axes at the bottom or left graph edge as usual."
                                },
                                "xRangePad": {
                                    "default": "0",
                                    "labels": ["Axis display"],
                                    "type": "float",
                                    "description": "Add the specified amount of extra space (in pixels) around the X-axis value range to ensure points at the edges remain visible."
                                },
                                "yRangePad": {
                                    "default": "null",
                                    "labels": ["Axis display"],
                                    "type": "float",
                                    "description": "If set, add the specified amount of extra space (in pixels) around the Y-axis value range to ensure points at the edges remain visible. If unset, use the traditional Y padding algorithm."
                                },
                                "axisLabelFormatter": {
                                    "default": "Depends on the data type",
                                    "labels": ["Axis display"],
                                    "type": "function(number or Date, granularity, opts, dygraph)",
                                    "parameters": [["number or date", "Either a number (for a numeric axis) or a Date object (for a date axis)"], ["granularity", "specifies how fine-grained the axis is. For date axes, this is a reference to the time granularity enumeration, defined in dygraph-tickers.js, e.g. Dygraph.WEEKLY."], ["opts", "a function which provides access to various options on the dygraph, e.g. opts('labelsKMB')."], ["dygraph", "the referenced graph"]],
                                    "description": "Function to call to format the tick values that appear along an axis. This is usually set on a <a href='per-axis.html'>per-axis</a> basis."
                                },
                                "clickCallback": {
                                    "snippet": "function(e, date_millis){<br>&nbsp;&nbsp;alert(new Date(date_millis));<br>}",
                                    "default": "null",
                                    "labels": ["Callbacks"],
                                    "type": "function(e, x, points)",
                                    "parameters": [["e", "The event object for the click"], ["x", "The x value that was clicked (for dates, this is milliseconds since epoch)"], ["points", "The closest points along that date. See <a href='#point_properties'>Point properties</a> for details."]],
                                    "description": "A function to call when the canvas is clicked."
                                },
                                "labels": {
                                    "default": "[\"X\", \"Y1\", \"Y2\", ...]*",
                                    "labels": ["Legend"],
                                    "type": "array<string>",
                                    "description": "A name for each data series, including the independent (X) series. For CSV files and DataTable objections, this is determined by context. For raw data, this must be specified. If it is not, default values are supplied and a warning is logged."
                                },
                                "dateWindow": {
                                    "default": "Full range of the input is shown",
                                    "labels": ["Axis display"],
                                    "type": "Array of two numbers",
                                    "example": "[<br>&nbsp;&nbsp;Date.parse('2006-01-01'),<br>&nbsp;&nbsp;(new Date()).valueOf()<br>]",
                                    "description": "Initially zoom in on a section of the graph. Is of the form [earliest, latest], where earliest/latest are milliseconds since epoch. If the data for the x-axis is numeric, the values in dateWindow must also be numbers."
                                },
                                "showRoller": {
                                    "default": "false",
                                    "labels": ["Interactive Elements", "Rolling Averages"],
                                    "type": "boolean",
                                    "description": "If the rolling average period text box should be shown."
                                },
                                "sigma": {
                                    "default": "2.0",
                                    "labels": ["Error Bars"],
                                    "type": "float",
                                    "description": "When errorBars is set, shade this many standard deviations above/below each point."
                                },
                                "customBars": {
                                    "default": "false",
                                    "labels": ["CSV parsing", "Error Bars"],
                                    "type": "boolean",
                                    "description": "When set, parse each CSV cell as \"low;middle;high\". Error bars will be drawn for each point between low and high, with the series itself going through middle."
                                },
                                "colorValue": {
                                    "default": "1.0",
                                    "labels": ["Data Series Colors"],
                                    "type": "float (0.0 - 1.0)",
                                    "description": "If colors is not specified, value of the data series colors, as in hue/saturation/value. (0.0-1.0, default 0.5)"
                                },
                                "errorBars": {
                                    "default": "false",
                                    "labels": ["CSV parsing", "Error Bars"],
                                    "type": "boolean",
                                    "description": "Does the data contain standard deviations? Setting this to true alters the input format (see above)."
                                },
                                "displayAnnotations": {
                                    "default": "false",
                                    "labels": ["Annotations"],
                                    "type": "boolean",
                                    "description": "Only applies when Dygraphs is used as a GViz chart. Causes string columns following a data series to be interpreted as annotations on points in that series. This is the same format used by Google's AnnotatedTimeLine chart."
                                },
                                "panEdgeFraction": {
                                    "default": "null",
                                    "labels": ["Axis display", "Interactive Elements"],
                                    "type": "float",
                                    "description": "A value representing the farthest a graph may be panned, in percent of the display. For example, a value of 0.1 means that the graph can only be panned 10% passed the edges of the displayed values. null means no bounds."
                                },
                                "title": {
                                    "labels": ["Chart labels"],
                                    "type": "string",
                                    "default": "null",
                                    "description": "Text to display above the chart. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-title' classes."
                                },
                                "titleHeight": {
                                    "default": "18",
                                    "labels": ["Chart labels"],
                                    "type": "integer",
                                    "description": "Height of the chart title, in pixels. This also controls the default font size of the title. If you style the title on your own, this controls how much space is set aside above the chart for the title's div."
                                },
                                "xlabel": {
                                    "labels": ["Chart labels"],
                                    "type": "string",
                                    "default": "null",
                                    "description": "Text to display below the chart's x-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-xlabel' classes."
                                },
                                "xLabelHeight": {
                                    "labels": ["Chart labels"],
                                    "type": "integer",
                                    "default": "18",
                                    "description": "Height of the x-axis label, in pixels. This also controls the default font size of the x-axis label. If you style the label on your own, this controls how much space is set aside below the chart for the x-axis label's div."
                                },
                                "ylabel": {
                                    "labels": ["Chart labels"],
                                    "type": "string",
                                    "default": "null",
                                    "description": "Text to display to the left of the chart's y-axis. You can supply any HTML for this value, not just text. If you wish to style it using CSS, use the 'dygraph-label' or 'dygraph-ylabel' classes. The text will be rotated 90 degrees by default, so CSS rules may behave in unintuitive ways. No additional space is set aside for a y-axis label. If you need more space, increase the width of the y-axis tick labels using the yAxisLabelWidth option. If you need a wider div for the y-axis label, either style it that way with CSS (but remember that it's rotated, so width is controlled by the 'height' property) or set the yLabelWidth option."
                                },
                                "y2label": {
                                    "labels": ["Chart labels"],
                                    "type": "string",
                                    "default": "null",
                                    "description": "Text to display to the right of the chart's secondary y-axis. This label is only displayed if a secondary y-axis is present. See <a href='http://dygraphs.com/tests/two-axes.html'>this test</a> for an example of how to do this. The comments for the 'ylabel' option generally apply here as well. This label gets a 'dygraph-y2label' instead of a 'dygraph-ylabel' class."
                                },
                                "yLabelWidth": {
                                    "labels": ["Chart labels"],
                                    "type": "integer",
                                    "default": "18",
                                    "description": "Width of the div which contains the y-axis label. Since the y-axis label appears rotated 90 degrees, this actually affects the height of its div."
                                },
                                "drawGrid": {
                                    "default": "true for x and y, false for y2",
                                    "labels": ["Grid"],
                                    "type": "boolean",
                                    "description": "Whether to display gridlines in the chart. This may be set on a per-axis basis to define the visibility of each axis' grid separately."
                                },
                                "independentTicks": {
                                    "default": "true for y, false for y2",
                                    "labels": ["Axis display", "Grid"],
                                    "type": "boolean",
                                    "description": "Only valid for y and y2, has no effect on x: This option defines whether the y axes should align their ticks or if they should be independent. Possible combinations: 1.) y=true, y2=false (default): y is the primary axis and the y2 ticks are aligned to the the ones of y. (only 1 grid) 2.) y=false, y2=true: y2 is the primary axis and the y ticks are aligned to the the ones of y2. (only 1 grid) 3.) y=true, y2=true: Both axis are independent and have their own ticks. (2 grids) 4.) y=false, y2=false: Invalid configuration causes an error."
                                },
                                "drawAxis": {
                                    "default": "true for x and y, false for y2",
                                    "labels": ["Axis display"],
                                    "type": "boolean",
                                    "description": "Whether to draw the specified axis. This may be set on a per-axis basis to define the visibility of each axis separately. Setting this to false also prevents axis ticks from being drawn and reclaims the space for the chart grid/lines."
                                },
                                "gridLineWidth": {
                                    "default": "0.3",
                                    "labels": ["Grid"],
                                    "type": "float",
                                    "description": "Thickness (in pixels) of the gridlines drawn under the chart. The vertical/horizontal gridlines can be turned off entirely by using the drawGrid option. This may be set on a per-axis basis to define each axis' grid separately."
                                },
                                "axisLineWidth": {
                                    "default": "0.3",
                                    "labels": ["Axis display"],
                                    "type": "float",
                                    "description": "Thickness (in pixels) of the x- and y-axis lines."
                                },
                                "axisLineColor": {
                                    "default": "black",
                                    "labels": ["Axis display"],
                                    "type": "string",
                                    "description": "Color of the x- and y-axis lines. Accepts any value which the HTML canvas strokeStyle attribute understands, e.g. 'black' or 'rgb(0, 100, 255)'."
                                },
                                "fillAlpha": {
                                    "default": "0.15",
                                    "labels": ["Error Bars", "Data Series Colors"],
                                    "type": "float (0.0 - 1.0)",
                                    "description": "Error bars (or custom bars) for each series are drawn in the same color as the series, but with partial transparency. This sets the transparency. A value of 0.0 means that the error bars will not be drawn, whereas a value of 1.0 means that the error bars will be as dark as the line for the series itself. This can be used to produce chart lines whose thickness varies at each point."
                                },
                                "axisLabelWidth": {
                                    "default": "50 (y-axis), 60 (x-axis)",
                                    "labels": ["Axis display", "Chart labels"],
                                    "type": "integer",
                                    "description": "Width (in pixels) of the containing divs for x- and y-axis labels. For the y-axis, this also controls the width of the y-axis. Note that for the x-axis, this is independent from pixelsPerLabel, which controls the spacing between labels."
                                },
                                "sigFigs": {
                                    "default": "null",
                                    "labels": ["Value display/formatting"],
                                    "type": "integer",
                                    "description": "By default, dygraphs displays numbers with a fixed number of digits after the decimal point. If you'd prefer to have a fixed number of significant figures, set this option to that number of sig figs. A value of 2, for instance, would cause 1 to be display as 1.0 and 1234 to be displayed as 1.23e+3."
                                },
                                "digitsAfterDecimal": {
                                    "default": "2",
                                    "labels": ["Value display/formatting"],
                                    "type": "integer",
                                    "description": "Unless it's run in scientific mode (see the <code>sigFigs</code> option), dygraphs displays numbers with <code>digitsAfterDecimal</code> digits after the decimal point. Trailing zeros are not displayed, so with a value of 2 you'll get '0', '0.1', '0.12', '123.45' but not '123.456' (it will be rounded to '123.46'). Numbers with absolute value less than 0.1^digitsAfterDecimal (i.e. those which would show up as '0.00') will be displayed in scientific notation."
                                },
                                "maxNumberWidth": {
                                    "default": "6",
                                    "labels": ["Value display/formatting"],
                                    "type": "integer",
                                    "description": "When displaying numbers in normal (not scientific) mode, large numbers will be displayed with many trailing zeros (e.g. 100000000 instead of 1e9). This can lead to unwieldy y-axis labels. If there are more than <code>maxNumberWidth</code> digits to the left of the decimal in a number, dygraphs will switch to scientific notation, even when not operating in scientific mode. If you'd like to see all those digits, set this to something large, like 20 or 30."
                                },
                                "file": {
                                    "default": "(set when constructed)",
                                    "labels": ["Data"],
                                    "type": "string (URL of CSV or CSV), GViz DataTable or 2D Array",
                                    "description": "Sets the data being displayed in the chart. This can only be set when calling updateOptions; it cannot be set from the constructor. For a full description of valid data formats, see the <a href='http://dygraphs.com/data.html'>Data Formats</a> page."
                                },
                                "timingName": {
                                    "default": "null",
                                    "labels": ["Debugging", "Deprecated"],
                                    "type": "string",
                                    "description": "Set this option to log timing information. The value of the option will be logged along with the timimg, so that you can distinguish multiple dygraphs on the same page."
                                },
                                "showRangeSelector": {
                                    "default": "false",
                                    "labels": ["Range Selector"],
                                    "type": "boolean",
                                    "description": "Show or hide the range selector widget."
                                },
                                "rangeSelectorHeight": {
                                    "default": "40",
                                    "labels": ["Range Selector"],
                                    "type": "integer",
                                    "description": "Height, in pixels, of the range selector widget. This option can only be specified at Dygraph creation time."
                                },
                                "rangeSelectorPlotStrokeColor": {
                                    "default": "#808FAB",
                                    "labels": ["Range Selector"],
                                    "type": "string",
                                    "description": "The range selector mini plot stroke color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off stroke."
                                },
                                "rangeSelectorPlotFillColor": {
                                    "default": "#A7B1C4",
                                    "labels": ["Range Selector"],
                                    "type": "string",
                                    "description": "The range selector mini plot fill color. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\". You can also specify null or \"\" to turn off fill."
                                },
                                "rangeSelectorPlotFillGradientColor": {
                                    "default": "white",
                                    "labels": ["Range Selector"],
                                    "type": "string",
                                    "description": "The top color for the range selector mini plot fill color gradient. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"rgba(255,100,200,42)\" or \"yellow\". You can also specify null or \"\" to disable the gradient and fill with one single color."
                                },
                                "rangeSelectorBackgroundStrokeColor": {
                                    "default": "gray",
                                    "labels": ["Range Selector"],
                                    "type": "string",
                                    "description": "The color of the lines below and on both sides of the range selector mini plot. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"."
                                },
                                "rangeSelectorBackgroundLineWidth": {
                                    "default": "1",
                                    "labels": ["Range Selector"],
                                    "type": "float",
                                    "description": "The width of the lines below and on both sides of the range selector mini plot."
                                },
                                "rangeSelectorPlotLineWidth": {
                                    "default": "1.5",
                                    "labels": ["Range Selector"],
                                    "type": "float",
                                    "description": "The width of the range selector mini plot line."
                                },
                                "rangeSelectorForegroundStrokeColor": {
                                    "default": "black",
                                    "labels": ["Range Selector"],
                                    "type": "string",
                                    "description": "The color of the lines in the interactive layer of the range selector. This can be of the form \"#AABBCC\" or \"rgb(255,100,200)\" or \"yellow\"."
                                },
                                "rangeSelectorForegroundLineWidth": {
                                    "default": "1",
                                    "labels": ["Range Selector"],
                                    "type": "float",
                                    "description": "The width the lines in the interactive layer of the range selector."
                                },
                                "rangeSelectorAlpha": {
                                    "default": "0.6",
                                    "labels": ["Range Selector"],
                                    "type": "float (0.0 - 1.0)",
                                    "description": "The transparency of the veil that is drawn over the unselected portions of the range selector mini plot. A value of 0 represents full transparency and the unselected portions of the mini plot will appear as normal. A value of 1 represents full opacity and the unselected portions of the mini plot will be hidden."
                                },
                                "showInRangeSelector": {
                                    "default": "null",
                                    "labels": ["Range Selector"],
                                    "type": "boolean",
                                    "description": "Mark this series for inclusion in the range selector. The mini plot curve will be an average of all such series. If this is not specified for any series, the default behavior is to average all the visible series. Setting it for one series will result in that series being charted alone in the range selector. Once it's set for a single series, it needs to be set for all series which should be included (regardless of visibility)."
                                },
                                "animatedZooms": {
                                    "default": "false",
                                    "labels": ["Interactive Elements"],
                                    "type": "boolean",
                                    "description": "Set this option to animate the transition between zoom windows. Applies to programmatic and interactive zooms. Note that if you also set a drawCallback, it will be called several times on each zoom. If you set a zoomCallback, it will only be called after the animation is complete."
                                },
                                "plotter": {
                                    "default": "[DygraphCanvasRenderer.Plotters.fillPlotter, DygraphCanvasRenderer.Plotters.errorPlotter, DygraphCanvasRenderer.Plotters.linePlotter]",
                                    "labels": ["Data Line display"],
                                    "type": "array or function",
                                    "description": "A function (or array of functions) which plot each data series on the chart. TODO(danvk): more details! May be set per-series."
                                },
                                "axes": {
                                    "default": "null",
                                    "labels": ["Configuration"],
                                    "type": "Object",
                                    "description": "Defines per-axis options. Valid keys are 'x', 'y' and 'y2'. Only some options may be set on a per-axis basis. If an option may be set in this way, it will be noted on this page. See also documentation on <a href='http://dygraphs.com/per-axis.html'>per-series and per-axis options</a>."
                                },
                                "series": {
                                    "default": "null",
                                    "labels": ["Series"],
                                    "type": "Object",
                                    "description": "Defines per-series options. Its keys match the y-axis label names, and the values are dictionaries themselves that contain options specific to that series."
                                },
                                "plugins": {
                                    "default": "[]",
                                    "labels": ["Configuration"],
                                    "type": "Array<plugin>",
                                    "description": "Defines per-graph plugins. Useful for per-graph customization"
                                },
                                "dataHandler": {
                                    "default": "(depends on data)",
                                    "labels": ["Data"],
                                    "type": "Dygraph.DataHandler",
                                    "description": "Custom DataHandler. This is an advanced customization. See http://bit.ly/151E7Aq."
                                }
                            }; // </JSON>
                        // NOTE: in addition to parsing as JS, this snippet is expected to be valid
                        // JSON. This assumption cannot be checked in JS, but it will be checked when
                        // documentation is generated by the generate-documentation.py script. For the
                        // most part, this just means that you should always use double quotes.

                        // Do a quick sanity check on the options reference.
                        var warn = function warn(msg) {
                            if (window.console) window.console.warn(msg);
                        };
                        var flds = ['type', 'default', 'description'];
                        var valid_cats = ['Annotations', 'Axis display', 'Chart labels', 'CSV parsing', 'Callbacks', 'Data', 'Data Line display', 'Data Series Colors', 'Error Bars', 'Grid', 'Interactive Elements', 'Range Selector', 'Legend', 'Overall display', 'Rolling Averages', 'Series', 'Value display/formatting', 'Zooming', 'Debugging', 'Configuration', 'Deprecated'];
                        var i;
                        var cats = {};
                        for (i = 0; i < valid_cats.length; i++) cats[valid_cats[i]] = true;

                        for (var k in OPTIONS_REFERENCE) {
                            if (!OPTIONS_REFERENCE.hasOwnProperty(k)) continue;
                            var op = OPTIONS_REFERENCE[k];
                            for (i = 0; i < flds.length; i++) {
                                if (!op.hasOwnProperty(flds[i])) {
                                    warn('Option ' + k + ' missing "' + flds[i] + '" property');
                                } else if (typeof op[flds[i]] != 'string') {
                                    warn(k + '.' + flds[i] + ' must be of type string');
                                }
                            }
                            var labels = op.labels;
                            if (typeof labels !== 'object') {
                                warn('Option "' + k + '" is missing a "labels": [...] option');
                            } else {
                                for (i = 0; i < labels.length; i++) {
                                    if (!cats.hasOwnProperty(labels[i])) {
                                        warn('Option "' + k + '" has label "' + labels[i] + '", which is invalid.');
                                    }
                                }
                            }
                        }
                    }
                }

                exports['default'] = OPTIONS_REFERENCE;
                module.exports = exports['default'];

            }).call(this, require('_process'))

        }, { "_process": 1 }], 15: [function (require, module, exports) {
            (function (process) {
                /**
                 * @license
                 * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
                 * MIT-licensed (http://opensource.org/licenses/MIT)
                 */

                /**
                 * @fileoverview DygraphOptions is responsible for parsing and returning
                 * information about options.
                 */

                // TODO: remove this jshint directive & fix the warnings.
                /*jshint sub:true */
                "use strict";

                Object.defineProperty(exports, '__esModule', {
                    value: true
                });

                function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

                function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

                var _dygraphUtils = require('./dygraph-utils');

                var utils = _interopRequireWildcard(_dygraphUtils);

                var _dygraphDefaultAttrs = require('./dygraph-default-attrs');

                var _dygraphDefaultAttrs2 = _interopRequireDefault(_dygraphDefaultAttrs);

                var _dygraphOptionsReference = require('./dygraph-options-reference');

                var _dygraphOptionsReference2 = _interopRequireDefault(_dygraphOptionsReference);

                /*
                 * Interesting member variables: (REMOVING THIS LIST AS I CLOSURIZE)
                 * global_ - global attributes (common among all graphs, AIUI)
                 * user - attributes set by the user
                 * series_ - { seriesName -> { idx, yAxis, options }}
                 */

                /**
                 * This parses attributes into an object that can be easily queried.
                 *
                 * It doesn't necessarily mean that all options are available, specifically
                 * if labels are not yet available, since those drive details of the per-series
                 * and per-axis options.
                 *
                 * @param {Dygraph} dygraph The chart to which these options belong.
                 * @constructor
                 */
                var DygraphOptions = function DygraphOptions(dygraph) {
                    /**
                     * The dygraph.
                     * @type {!Dygraph}
                     */
                    this.dygraph_ = dygraph;

                    /**
                     * Array of axis index to { series : [ series names ] , options : { axis-specific options. }
                     * @type {Array.<{series : Array.<string>, options : Object}>} @private
                     */
                    this.yAxes_ = [];

                    /**
                     * Contains x-axis specific options, which are stored in the options key.
                     * This matches the yAxes_ object structure (by being a dictionary with an
                     * options element) allowing for shared code.
                     * @type {options: Object} @private
                     */
                    this.xAxis_ = {};
                    this.series_ = {};

                    // Once these two objects are initialized, you can call get();
                    this.global_ = this.dygraph_.attrs_;
                    this.user_ = this.dygraph_.user_attrs_ || {};

                    /**
                     * A list of series in columnar order.
                     * @type {Array.<string>}
                     */
                    this.labels_ = [];

                    this.highlightSeries_ = this.get("highlightSeriesOpts") || {};
                    this.reparseSeries();
                };

                /**
                 * Not optimal, but does the trick when you're only using two axes.
                 * If we move to more axes, this can just become a function.
                 *
                 * @type {Object.<number>}
                 * @private
                 */
                DygraphOptions.AXIS_STRING_MAPPINGS_ = {
                    'y': 0,
                    'Y': 0,
                    'y1': 0,
                    'Y1': 0,
                    'y2': 1,
                    'Y2': 1
                };

                /**
                 * @param {string|number} axis
                 * @private
                 */
                DygraphOptions.axisToIndex_ = function (axis) {
                    if (typeof axis == "string") {
                        if (DygraphOptions.AXIS_STRING_MAPPINGS_.hasOwnProperty(axis)) {
                            return DygraphOptions.AXIS_STRING_MAPPINGS_[axis];
                        }
                        throw "Unknown axis : " + axis;
                    }
                    if (typeof axis == "number") {
                        if (axis === 0 || axis === 1) {
                            return axis;
                        }
                        throw "Dygraphs only supports two y-axes, indexed from 0-1.";
                    }
                    if (axis) {
                        throw "Unknown axis : " + axis;
                    }
                    // No axis specification means axis 0.
                    return 0;
                };

                /**
                 * Reparses options that are all related to series. This typically occurs when
                 * options are either updated, or source data has been made available.
                 *
                 * TODO(konigsberg): The method name is kind of weak; fix.
                 */
                DygraphOptions.prototype.reparseSeries = function () {
                    var labels = this.get("labels");
                    if (!labels) {
                        return; // -- can't do more for now, will parse after getting the labels.
                    }

                    this.labels_ = labels.slice(1);

                    this.yAxes_ = [{ series: [], options: {} }]; // Always one axis at least.
                    this.xAxis_ = { options: {} };
                    this.series_ = {};

                    // Series are specified in the series element:
                    //
                    // {
                    //   labels: [ "X", "foo", "bar" ],
                    //   pointSize: 3,
                    //   series : {
                    //     foo : {}, // options for foo
                    //     bar : {} // options for bar
                    //   }
                    // }
                    //
                    // So, if series is found, it's expected to contain per-series data, otherwise set a
                    // default.
                    var seriesDict = this.user_.series || {};
                    for (var idx = 0; idx < this.labels_.length; idx++) {
                        var seriesName = this.labels_[idx];
                        var optionsForSeries = seriesDict[seriesName] || {};
                        var yAxis = DygraphOptions.axisToIndex_(optionsForSeries["axis"]);

                        this.series_[seriesName] = {
                            idx: idx,
                            yAxis: yAxis,
                            options: optionsForSeries
                        };

                        if (!this.yAxes_[yAxis]) {
                            this.yAxes_[yAxis] = { series: [seriesName], options: {} };
                        } else {
                            this.yAxes_[yAxis].series.push(seriesName);
                        }
                    }

                    var axis_opts = this.user_["axes"] || {};
                    utils.update(this.yAxes_[0].options, axis_opts["y"] || {});
                    if (this.yAxes_.length > 1) {
                        utils.update(this.yAxes_[1].options, axis_opts["y2"] || {});
                    }
                    utils.update(this.xAxis_.options, axis_opts["x"] || {});

                    // For "production" code, this gets removed by uglifyjs.
                    if (typeof process !== 'undefined') {
                        if ("development" != 'production') {
                            this.validateOptions_();
                        }
                    }
                };

                /**
                 * Get a global value.
                 *
                 * @param {string} name the name of the option.
                 */
                DygraphOptions.prototype.get = function (name) {
                    var result = this.getGlobalUser_(name);
                    if (result !== null) {
                        return result;
                    }
                    return this.getGlobalDefault_(name);
                };

                DygraphOptions.prototype.getGlobalUser_ = function (name) {
                    if (this.user_.hasOwnProperty(name)) {
                        return this.user_[name];
                    }
                    return null;
                };

                DygraphOptions.prototype.getGlobalDefault_ = function (name) {
                    if (this.global_.hasOwnProperty(name)) {
                        return this.global_[name];
                    }
                    if (_dygraphDefaultAttrs2['default'].hasOwnProperty(name)) {
                        return _dygraphDefaultAttrs2['default'][name];
                    }
                    return null;
                };

                /**
                 * Get a value for a specific axis. If there is no specific value for the axis,
                 * the global value is returned.
                 *
                 * @param {string} name the name of the option.
                 * @param {string|number} axis the axis to search. Can be the string representation
                 * ("y", "y2") or the axis number (0, 1).
                 */
                DygraphOptions.prototype.getForAxis = function (name, axis) {
                    var axisIdx;
                    var axisString;

                    // Since axis can be a number or a string, straighten everything out here.
                    if (typeof axis == 'number') {
                        axisIdx = axis;
                        axisString = axisIdx === 0 ? "y" : "y2";
                    } else {
                        if (axis == "y1") {
                            axis = "y";
                        } // Standardize on 'y'. Is this bad? I think so.
                        if (axis == "y") {
                            axisIdx = 0;
                        } else if (axis == "y2") {
                            axisIdx = 1;
                        } else if (axis == "x") {
                            axisIdx = -1; // simply a placeholder for below.
                        } else {
                            throw "Unknown axis " + axis;
                        }
                        axisString = axis;
                    }

                    var userAxis = axisIdx == -1 ? this.xAxis_ : this.yAxes_[axisIdx];

                    // Search the user-specified axis option first.
                    if (userAxis) {
                        // This condition could be removed if we always set up this.yAxes_ for y2.
                        var axisOptions = userAxis.options;
                        if (axisOptions.hasOwnProperty(name)) {
                            return axisOptions[name];
                        }
                    }

                    // User-specified global options second.
                    // But, hack, ignore globally-specified 'logscale' for 'x' axis declaration.
                    if (!(axis === 'x' && name === 'logscale')) {
                        var result = this.getGlobalUser_(name);
                        if (result !== null) {
                            return result;
                        }
                    }
                    // Default axis options third.
                    var defaultAxisOptions = _dygraphDefaultAttrs2['default'].axes[axisString];
                    if (defaultAxisOptions.hasOwnProperty(name)) {
                        return defaultAxisOptions[name];
                    }

                    // Default global options last.
                    return this.getGlobalDefault_(name);
                };

                /**
                 * Get a value for a specific series. If there is no specific value for the series,
                 * the value for the axis is returned (and afterwards, the global value.)
                 *
                 * @param {string} name the name of the option.
                 * @param {string} series the series to search.
                 */
                DygraphOptions.prototype.getForSeries = function (name, series) {
                    // Honors indexes as series.
                    if (series === this.dygraph_.getHighlightSeries()) {
                        if (this.highlightSeries_.hasOwnProperty(name)) {
                            return this.highlightSeries_[name];
                        }
                    }

                    if (!this.series_.hasOwnProperty(series)) {
                        throw "Unknown series: " + series;
                    }

                    var seriesObj = this.series_[series];
                    var seriesOptions = seriesObj["options"];
                    if (seriesOptions.hasOwnProperty(name)) {
                        return seriesOptions[name];
                    }

                    return this.getForAxis(name, seriesObj["yAxis"]);
                };

                /**
                 * Returns the number of y-axes on the chart.
                 * @return {number} the number of axes.
                 */
                DygraphOptions.prototype.numAxes = function () {
                    return this.yAxes_.length;
                };

                /**
                 * Return the y-axis for a given series, specified by name.
                 */
                DygraphOptions.prototype.axisForSeries = function (series) {
                    return this.series_[series].yAxis;
                };

                /**
                 * Returns the options for the specified axis.
                 */
                // TODO(konigsberg): this is y-axis specific. Support the x axis.
                DygraphOptions.prototype.axisOptions = function (yAxis) {
                    return this.yAxes_[yAxis].options;
                };

                /**
                 * Return the series associated with an axis.
                 */
                DygraphOptions.prototype.seriesForAxis = function (yAxis) {
                    return this.yAxes_[yAxis].series;
                };

                /**
                 * Return the list of all series, in their columnar order.
                 */
                DygraphOptions.prototype.seriesNames = function () {
                    return this.labels_;
                };

                // For "production" code, this gets removed by uglifyjs.
                if (typeof process !== 'undefined') {
                    if ("development" != 'production') {

                        /**
                         * Validate all options.
                         * This requires OPTIONS_REFERENCE, which is only available in debug builds.
                         * @private
                         */
                        DygraphOptions.prototype.validateOptions_ = function () {
                            if (typeof _dygraphOptionsReference2['default'] === 'undefined') {
                                throw 'Called validateOptions_ in prod build.';
                            }

                            var that = this;
                            var validateOption = function validateOption(optionName) {
                                if (!_dygraphOptionsReference2['default'][optionName]) {
                                    that.warnInvalidOption_(optionName);
                                }
                            };

                            var optionsDicts = [this.xAxis_.options, this.yAxes_[0].options, this.yAxes_[1] && this.yAxes_[1].options, this.global_, this.user_, this.highlightSeries_];
                            var names = this.seriesNames();
                            for (var i = 0; i < names.length; i++) {
                                var name = names[i];
                                if (this.series_.hasOwnProperty(name)) {
                                    optionsDicts.push(this.series_[name].options);
                                }
                            }
                            for (var i = 0; i < optionsDicts.length; i++) {
                                var dict = optionsDicts[i];
                                if (!dict) continue;
                                for (var optionName in dict) {
                                    if (dict.hasOwnProperty(optionName)) {
                                        validateOption(optionName);
                                    }
                                }
                            }
                        };

                        var WARNINGS = {}; // Only show any particular warning once.

                        /**
                         * Logs a warning about invalid options.
                         * TODO: make this throw for testing
                         * @private
                         */
                        DygraphOptions.prototype.warnInvalidOption_ = function (optionName) {
                            if (!WARNINGS[optionName]) {
                                WARNINGS[optionName] = true;
                                var isSeries = this.labels_.indexOf(optionName) >= 0;
                                if (isSeries) {
                                    console.warn('Use new-style per-series options (saw ' + optionName + ' as top-level options key). See http://bit.ly/1tceaJs');
                                } else {
                                    console.warn('Unknown option ' + optionName + ' (full list of options at dygraphs.com/options.html');
                                }
                                throw "invalid option " + optionName;
                            }
                        };

                        // Reset list of previously-shown warnings. Used for testing.
                        DygraphOptions.resetWarnings_ = function () {
                            WARNINGS = {};
                        };
                    }
                }

                exports['default'] = DygraphOptions;
                module.exports = exports['default'];

            }).call(this, require('_process'))

        }, { "./dygraph-default-attrs": 10, "./dygraph-options-reference": 14, "./dygraph-utils": 17, "_process": 1 }], 16: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview Description of this file.
             * @author danvk@google.com (Dan Vanderkam)
             *
             * A ticker is a function with the following interface:
             *
             * function(a, b, pixels, options_view, dygraph, forced_values);
             * -> [ { v: tick1_v, label: tick1_label[, label_v: label_v1] },
             *      { v: tick2_v, label: tick2_label[, label_v: label_v2] },
             *      ...
             *    ]
             *
             * The returned value is called a "tick list".
             *
             * Arguments
             * ---------
             *
             * [a, b] is the range of the axis for which ticks are being generated. For a
             * numeric axis, these will simply be numbers. For a date axis, these will be
             * millis since epoch (convertable to Date objects using "new Date(a)" and "new
             * Date(b)").
             *
             * opts provides access to chart- and axis-specific options. It can be used to
             * access number/date formatting code/options, check for a log scale, etc.
             *
             * pixels is the length of the axis in pixels. opts('pixelsPerLabel') is the
             * minimum amount of space to be allotted to each label. For instance, if
             * pixels=400 and opts('pixelsPerLabel')=40 then the ticker should return
             * between zero and ten (400/40) ticks.
             *
             * dygraph is the Dygraph object for which an axis is being constructed.
             *
             * forced_values is used for secondary y-axes. The tick positions are typically
             * set by the primary y-axis, so the secondary y-axis has no choice in where to
             * put these. It simply has to generate labels for these data values.
             *
             * Tick lists
             * ----------
             * Typically a tick will have both a grid/tick line and a label at one end of
             * that line (at the bottom for an x-axis, at left or right for the y-axis).
             *
             * A tick may be missing one of these two components:
             * - If "label_v" is specified instead of "v", then there will be no tick or
             *   gridline, just a label.
             * - Similarly, if "label" is not specified, then there will be a gridline
             *   without a label.
             *
             * This flexibility is useful in a few situations:
             * - For log scales, some of the tick lines may be too close to all have labels.
             * - For date scales where years are being displayed, it is desirable to display
             *   tick marks at the beginnings of years but labels (e.g. "2006") in the
             *   middle of the years.
             */

            /*jshint sub:true */
            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

            var _dygraphUtils = require('./dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            /** @typedef {Array.<{v:number, label:string, label_v:(string|undefined)}>} */
            var TickList = undefined; // the ' = undefined' keeps jshint happy.

            /** @typedef {function(
             *    number,
             *    number,
             *    number,
             *    function(string):*,
             *    Dygraph=,
             *    Array.<number>=
             *  ): TickList}
             */
            var Ticker = undefined; // the ' = undefined' keeps jshint happy.

            /** @type {Ticker} */
            var numericLinearTicks = function numericLinearTicks(a, b, pixels, opts, dygraph, vals) {
                var nonLogscaleOpts = function nonLogscaleOpts(opt) {
                    if (opt === 'logscale') return false;
                    return opts(opt);
                };
                return numericTicks(a, b, pixels, nonLogscaleOpts, dygraph, vals);
            };

            exports.numericLinearTicks = numericLinearTicks;
            /** @type {Ticker} */
            var numericTicks = function numericTicks(a, b, pixels, opts, dygraph, vals) {
                var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel');
                var ticks = [];
                var i, j, tickV, nTicks;
                if (vals) {
                    for (i = 0; i < vals.length; i++) {
                        ticks.push({ v: vals[i] });
                    }
                } else {
                    // TODO(danvk): factor this log-scale block out into a separate function.
                    if (opts("logscale")) {
                        nTicks = Math.floor(pixels / pixels_per_tick);
                        var minIdx = utils.binarySearch(a, PREFERRED_LOG_TICK_VALUES, 1);
                        var maxIdx = utils.binarySearch(b, PREFERRED_LOG_TICK_VALUES, -1);
                        if (minIdx == -1) {
                            minIdx = 0;
                        }
                        if (maxIdx == -1) {
                            maxIdx = PREFERRED_LOG_TICK_VALUES.length - 1;
                        }
                        // Count the number of tick values would appear, if we can get at least
                        // nTicks / 4 accept them.
                        var lastDisplayed = null;
                        if (maxIdx - minIdx >= nTicks / 4) {
                            for (var idx = maxIdx; idx >= minIdx; idx--) {
                                var tickValue = PREFERRED_LOG_TICK_VALUES[idx];
                                var pixel_coord = Math.log(tickValue / a) / Math.log(b / a) * pixels;
                                var tick = { v: tickValue };
                                if (lastDisplayed === null) {
                                    lastDisplayed = {
                                        tickValue: tickValue,
                                        pixel_coord: pixel_coord
                                    };
                                } else {
                                    if (Math.abs(pixel_coord - lastDisplayed.pixel_coord) >= pixels_per_tick) {
                                        lastDisplayed = {
                                            tickValue: tickValue,
                                            pixel_coord: pixel_coord
                                        };
                                    } else {
                                        tick.label = "";
                                    }
                                }
                                ticks.push(tick);
                            }
                            // Since we went in backwards order.
                            ticks.reverse();
                        }
                    }

                    // ticks.length won't be 0 if the log scale function finds values to insert.
                    if (ticks.length === 0) {
                        // Basic idea:
                        // Try labels every 1, 2, 5, 10, 20, 50, 100, etc.
                        // Calculate the resulting tick spacing (i.e. this.height_ / nTicks).
                        // The first spacing greater than pixelsPerYLabel is what we use.
                        // TODO(danvk): version that works on a log scale.
                        var kmg2 = opts("labelsKMG2");
                        var mults, base;
                        if (kmg2) {
                            mults = [1, 2, 4, 8, 16, 32, 64, 128, 256];
                            base = 16;
                        } else {
                            mults = [1, 2, 5, 10, 20, 50, 100];
                            base = 10;
                        }

                        // Get the maximum number of permitted ticks based on the
                        // graph's pixel size and pixels_per_tick setting.
                        var max_ticks = Math.ceil(pixels / pixels_per_tick);

                        // Now calculate the data unit equivalent of this tick spacing.
                        // Use abs() since graphs may have a reversed Y axis.
                        var units_per_tick = Math.abs(b - a) / max_ticks;

                        // Based on this, get a starting scale which is the largest
                        // integer power of the chosen base (10 or 16) that still remains
                        // below the requested pixels_per_tick spacing.
                        var base_power = Math.floor(Math.log(units_per_tick) / Math.log(base));
                        var base_scale = Math.pow(base, base_power);

                        // Now try multiples of the starting scale until we find one
                        // that results in tick marks spaced sufficiently far apart.
                        // The "mults" array should cover the range 1 .. base^2 to
                        // adjust for rounding and edge effects.
                        var scale, low_val, high_val, spacing;
                        for (j = 0; j < mults.length; j++) {
                            scale = base_scale * mults[j];
                            low_val = Math.floor(a / scale) * scale;
                            high_val = Math.ceil(b / scale) * scale;
                            nTicks = Math.abs(high_val - low_val) / scale;
                            spacing = pixels / nTicks;
                            if (spacing > pixels_per_tick) break;
                        }

                        // Construct the set of ticks.
                        // Allow reverse y-axis if it's explicitly requested.
                        if (low_val > high_val) scale *= -1;
                        for (i = 0; i <= nTicks; i++) {
                            tickV = low_val + i * scale;
                            ticks.push({ v: tickV });
                        }
                    }
                }

                var formatter = /**@type{AxisLabelFormatter}*/opts('axisLabelFormatter');

                // Add labels to the ticks.
                for (i = 0; i < ticks.length; i++) {
                    if (ticks[i].label !== undefined) continue; // Use current label.
                    // TODO(danvk): set granularity to something appropriate here.
                    ticks[i].label = formatter.call(dygraph, ticks[i].v, 0, opts, dygraph);
                }

                return ticks;
            };

            exports.numericTicks = numericTicks;
            /** @type {Ticker} */
            var dateTicker = function dateTicker(a, b, pixels, opts, dygraph, vals) {
                var chosen = pickDateTickGranularity(a, b, pixels, opts);

                if (chosen >= 0) {
                    return getDateAxis(a, b, chosen, opts, dygraph);
                } else {
                    // this can happen if self.width_ is zero.
                    return [];
                }
            };

            exports.dateTicker = dateTicker;
            // Time granularity enumeration
            var Granularity = {
                MILLISECONDLY: 0,
                TWO_MILLISECONDLY: 1,
                FIVE_MILLISECONDLY: 2,
                TEN_MILLISECONDLY: 3,
                FIFTY_MILLISECONDLY: 4,
                HUNDRED_MILLISECONDLY: 5,
                FIVE_HUNDRED_MILLISECONDLY: 6,
                SECONDLY: 7,
                TWO_SECONDLY: 8,
                FIVE_SECONDLY: 9,
                TEN_SECONDLY: 10,
                THIRTY_SECONDLY: 11,
                MINUTELY: 12,
                TWO_MINUTELY: 13,
                FIVE_MINUTELY: 14,
                TEN_MINUTELY: 15,
                THIRTY_MINUTELY: 16,
                HOURLY: 17,
                TWO_HOURLY: 18,
                SIX_HOURLY: 19,
                DAILY: 20,
                TWO_DAILY: 21,
                WEEKLY: 22,
                MONTHLY: 23,
                QUARTERLY: 24,
                BIANNUAL: 25,
                ANNUAL: 26,
                DECADAL: 27,
                CENTENNIAL: 28,
                NUM_GRANULARITIES: 29
            };

            exports.Granularity = Granularity;
            // Date components enumeration (in the order of the arguments in Date)
            // TODO: make this an @enum
            var DateField = {
                DATEFIELD_Y: 0,
                DATEFIELD_M: 1,
                DATEFIELD_D: 2,
                DATEFIELD_HH: 3,
                DATEFIELD_MM: 4,
                DATEFIELD_SS: 5,
                DATEFIELD_MS: 6,
                NUM_DATEFIELDS: 7
            };

            /**
             * The value of datefield will start at an even multiple of "step", i.e.
             *   if datefield=SS and step=5 then the first tick will be on a multiple of 5s.
             *
             * For granularities <= HOURLY, ticks are generated every `spacing` ms.
             *
             * At coarser granularities, ticks are generated by incrementing `datefield` by
             *   `step`. In this case, the `spacing` value is only used to estimate the
             *   number of ticks. It should roughly correspond to the spacing between
             *   adjacent ticks.
             *
             * @type {Array.<{datefield:number, step:number, spacing:number}>}
             */
            var TICK_PLACEMENT = [];
            TICK_PLACEMENT[Granularity.MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 1, spacing: 1 };
            TICK_PLACEMENT[Granularity.TWO_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 2, spacing: 2 };
            TICK_PLACEMENT[Granularity.FIVE_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 5, spacing: 5 };
            TICK_PLACEMENT[Granularity.TEN_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 10, spacing: 10 };
            TICK_PLACEMENT[Granularity.FIFTY_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 50, spacing: 50 };
            TICK_PLACEMENT[Granularity.HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 100, spacing: 100 };
            TICK_PLACEMENT[Granularity.FIVE_HUNDRED_MILLISECONDLY] = { datefield: DateField.DATEFIELD_MS, step: 500, spacing: 500 };
            TICK_PLACEMENT[Granularity.SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 1, spacing: 1000 * 1 };
            TICK_PLACEMENT[Granularity.TWO_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 2, spacing: 1000 * 2 };
            TICK_PLACEMENT[Granularity.FIVE_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 5, spacing: 1000 * 5 };
            TICK_PLACEMENT[Granularity.TEN_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 10, spacing: 1000 * 10 };
            TICK_PLACEMENT[Granularity.THIRTY_SECONDLY] = { datefield: DateField.DATEFIELD_SS, step: 30, spacing: 1000 * 30 };
            TICK_PLACEMENT[Granularity.MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 1, spacing: 1000 * 60 };
            TICK_PLACEMENT[Granularity.TWO_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 2, spacing: 1000 * 60 * 2 };
            TICK_PLACEMENT[Granularity.FIVE_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 5, spacing: 1000 * 60 * 5 };
            TICK_PLACEMENT[Granularity.TEN_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 10, spacing: 1000 * 60 * 10 };
            TICK_PLACEMENT[Granularity.THIRTY_MINUTELY] = { datefield: DateField.DATEFIELD_MM, step: 30, spacing: 1000 * 60 * 30 };
            TICK_PLACEMENT[Granularity.HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 1, spacing: 1000 * 3600 };
            TICK_PLACEMENT[Granularity.TWO_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 2, spacing: 1000 * 3600 * 2 };
            TICK_PLACEMENT[Granularity.SIX_HOURLY] = { datefield: DateField.DATEFIELD_HH, step: 6, spacing: 1000 * 3600 * 6 };
            TICK_PLACEMENT[Granularity.DAILY] = { datefield: DateField.DATEFIELD_D, step: 1, spacing: 1000 * 86400 };
            TICK_PLACEMENT[Granularity.TWO_DAILY] = { datefield: DateField.DATEFIELD_D, step: 2, spacing: 1000 * 86400 * 2 };
            TICK_PLACEMENT[Granularity.WEEKLY] = { datefield: DateField.DATEFIELD_D, step: 7, spacing: 1000 * 604800 };
            TICK_PLACEMENT[Granularity.MONTHLY] = { datefield: DateField.DATEFIELD_M, step: 1, spacing: 1000 * 7200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 12
            TICK_PLACEMENT[Granularity.QUARTERLY] = { datefield: DateField.DATEFIELD_M, step: 3, spacing: 1000 * 21600 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 4
            TICK_PLACEMENT[Granularity.BIANNUAL] = { datefield: DateField.DATEFIELD_M, step: 6, spacing: 1000 * 43200 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 / 2
            TICK_PLACEMENT[Granularity.ANNUAL] = { datefield: DateField.DATEFIELD_Y, step: 1, spacing: 1000 * 86400 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 1
            TICK_PLACEMENT[Granularity.DECADAL] = { datefield: DateField.DATEFIELD_Y, step: 10, spacing: 1000 * 864000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 10
            TICK_PLACEMENT[Granularity.CENTENNIAL] = { datefield: DateField.DATEFIELD_Y, step: 100, spacing: 1000 * 8640000 * 365.2524 }; // 1e3 * 60 * 60 * 24 * 365.2524 * 100

            /**
             * This is a list of human-friendly values at which to show tick marks on a log
             * scale. It is k * 10^n, where k=1..9 and n=-39..+39, so:
             * ..., 1, 2, 3, 4, 5, ..., 9, 10, 20, 30, ..., 90, 100, 200, 300, ...
             * NOTE: this assumes that utils.LOG_SCALE = 10.
             * @type {Array.<number>}
             */
            var PREFERRED_LOG_TICK_VALUES = (function () {
                var vals = [];
                for (var power = -39; power <= 39; power++) {
                    var range = Math.pow(10, power);
                    for (var mult = 1; mult <= 9; mult++) {
                        var val = range * mult;
                        vals.push(val);
                    }
                }
                return vals;
            })();

            /**
             * Determine the correct granularity of ticks on a date axis.
             *
             * @param {number} a Left edge of the chart (ms)
             * @param {number} b Right edge of the chart (ms)
             * @param {number} pixels Size of the chart in the relevant dimension (width).
             * @param {function(string):*} opts Function mapping from option name -&gt; value.
             * @return {number} The appropriate axis granularity for this chart. See the
             *     enumeration of possible values in dygraph-tickers.js.
             */
            var pickDateTickGranularity = function pickDateTickGranularity(a, b, pixels, opts) {
                var pixels_per_tick = /** @type{number} */opts('pixelsPerLabel');
                for (var i = 0; i < Granularity.NUM_GRANULARITIES; i++) {
                    var num_ticks = numDateTicks(a, b, i);
                    if (pixels / num_ticks >= pixels_per_tick) {
                        return i;
                    }
                }
                return -1;
            };

            /**
             * Compute the number of ticks on a date axis for a given granularity.
             * @param {number} start_time
             * @param {number} end_time
             * @param {number} granularity (one of the granularities enumerated above)
             * @return {number} (Approximate) number of ticks that would result.
             */
            var numDateTicks = function numDateTicks(start_time, end_time, granularity) {
                var spacing = TICK_PLACEMENT[granularity].spacing;
                return Math.round(1.0 * (end_time - start_time) / spacing);
            };

            /**
             * Compute the positions and labels of ticks on a date axis for a given granularity.
             * @param {number} start_time
             * @param {number} end_time
             * @param {number} granularity (one of the granularities enumerated above)
             * @param {function(string):*} opts Function mapping from option name -&gt; value.
             * @param {Dygraph=} dg
             * @return {!TickList}
             */
            var getDateAxis = function getDateAxis(start_time, end_time, granularity, opts, dg) {
                var formatter = /** @type{AxisLabelFormatter} */opts("axisLabelFormatter");
                var utc = opts("labelsUTC");
                var accessors = utc ? utils.DateAccessorsUTC : utils.DateAccessorsLocal;

                var datefield = TICK_PLACEMENT[granularity].datefield;
                var step = TICK_PLACEMENT[granularity].step;
                var spacing = TICK_PLACEMENT[granularity].spacing;

                // Choose a nice tick position before the initial instant.
                // Currently, this code deals properly with the existent daily granularities:
                // DAILY (with step of 1) and WEEKLY (with step of 7 but specially handled).
                // Other daily granularities (say TWO_DAILY) should also be handled specially
                // by setting the start_date_offset to 0.
                var start_date = new Date(start_time);
                var date_array = [];
                date_array[DateField.DATEFIELD_Y] = accessors.getFullYear(start_date);
                date_array[DateField.DATEFIELD_M] = accessors.getMonth(start_date);
                date_array[DateField.DATEFIELD_D] = accessors.getDate(start_date);
                date_array[DateField.DATEFIELD_HH] = accessors.getHours(start_date);
                date_array[DateField.DATEFIELD_MM] = accessors.getMinutes(start_date);
                date_array[DateField.DATEFIELD_SS] = accessors.getSeconds(start_date);
                date_array[DateField.DATEFIELD_MS] = accessors.getMilliseconds(start_date);

                var start_date_offset = date_array[datefield] % step;
                if (granularity == Granularity.WEEKLY) {
                    // This will put the ticks on Sundays.
                    start_date_offset = accessors.getDay(start_date);
                }

                date_array[datefield] -= start_date_offset;
                for (var df = datefield + 1; df < DateField.NUM_DATEFIELDS; df++) {
                    // The minimum value is 1 for the day of month, and 0 for all other fields.
                    date_array[df] = df === DateField.DATEFIELD_D ? 1 : 0;
                }

                // Generate the ticks.
                // For granularities not coarser than HOURLY we use the fact that:
                //   the number of milliseconds between ticks is constant
                //   and equal to the defined spacing.
                // Otherwise we rely on the 'roll over' property of the Date functions:
                //   when some date field is set to a value outside of its logical range,
                //   the excess 'rolls over' the next (more significant) field.
                // However, when using local time with DST transitions,
                // there are dates that do not represent any time value at all
                // (those in the hour skipped at the 'spring forward'),
                // and the JavaScript engines usually return an equivalent value.
                // Hence we have to check that the date is properly increased at each step,
                // returning a date at a nice tick position.
                var ticks = [];
                var tick_date = accessors.makeDate.apply(null, date_array);
                var tick_time = tick_date.getTime();
                if (granularity <= Granularity.HOURLY) {
                    if (tick_time < start_time) {
                        tick_time += spacing;
                        tick_date = new Date(tick_time);
                    }
                    while (tick_time <= end_time) {
                        ticks.push({
                            v: tick_time,
                            label: formatter.call(dg, tick_date, granularity, opts, dg)
                        });
                        tick_time += spacing;
                        tick_date = new Date(tick_time);
                    }
                } else {
                    if (tick_time < start_time) {
                        date_array[datefield] += step;
                        tick_date = accessors.makeDate.apply(null, date_array);
                        tick_time = tick_date.getTime();
                    }
                    while (tick_time <= end_time) {
                        if (granularity >= Granularity.DAILY || accessors.getHours(tick_date) % step === 0) {
                            ticks.push({
                                v: tick_time,
                                label: formatter.call(dg, tick_date, granularity, opts, dg)
                            });
                        }
                        date_array[datefield] += step;
                        tick_date = accessors.makeDate.apply(null, date_array);
                        tick_time = tick_date.getTime();
                    }
                }
                return ticks;
            };
            exports.getDateAxis = getDateAxis;

        }, { "./dygraph-utils": 17 }], 17: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2011 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /**
             * @fileoverview This file contains utility functions used by dygraphs. These
             * are typically static (i.e. not related to any particular dygraph). Examples
             * include date/time formatting functions, basic algorithms (e.g. binary
             * search) and generic DOM-manipulation functions.
             */

            /*global Dygraph:false, Node:false */
            "use strict";

            Object.defineProperty(exports, "__esModule", {
                value: true
            });
            exports.removeEvent = removeEvent;
            exports.cancelEvent = cancelEvent;
            exports.hsvToRGB = hsvToRGB;
            exports.findPos = findPos;
            exports.pageX = pageX;
            exports.pageY = pageY;
            exports.dragGetX_ = dragGetX_;
            exports.dragGetY_ = dragGetY_;
            exports.isOK = isOK;
            exports.isValidPoint = isValidPoint;
            exports.floatFormat = floatFormat;
            exports.zeropad = zeropad;
            exports.hmsString_ = hmsString_;
            exports.dateString_ = dateString_;
            exports.round_ = round_;
            exports.binarySearch = binarySearch;
            exports.dateParser = dateParser;
            exports.dateStrToMillis = dateStrToMillis;
            exports.update = update;
            exports.updateDeep = updateDeep;
            exports.isArrayLike = isArrayLike;
            exports.isDateLike = isDateLike;
            exports.clone = clone;
            exports.createCanvas = createCanvas;
            exports.getContextPixelRatio = getContextPixelRatio;
            exports.Iterator = Iterator;
            exports.createIterator = createIterator;
            exports.repeatAndCleanup = repeatAndCleanup;
            exports.isPixelChangingOptionList = isPixelChangingOptionList;
            exports.detectLineDelimiter = detectLineDelimiter;
            exports.isNodeContainedBy = isNodeContainedBy;
            exports.pow = pow;
            exports.toRGB_ = toRGB_;
            exports.isCanvasSupported = isCanvasSupported;
            exports.parseFloat_ = parseFloat_;
            exports.numberValueFormatter = numberValueFormatter;
            exports.numberAxisLabelFormatter = numberAxisLabelFormatter;
            exports.dateAxisLabelFormatter = dateAxisLabelFormatter;
            exports.dateValueFormatter = dateValueFormatter;

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }

            var _dygraphTickers = require('./dygraph-tickers');

            var DygraphTickers = _interopRequireWildcard(_dygraphTickers);

            var LOG_SCALE = 10;
            exports.LOG_SCALE = LOG_SCALE;
            var LN_TEN = Math.log(LOG_SCALE);

            exports.LN_TEN = LN_TEN;
            /**
             * @private
             * @param {number} x
             * @return {number}
             */
            var log10 = function log10(x) {
                return Math.log(x) / LN_TEN;
            };

            exports.log10 = log10;
            /**
             * @private
             * @param {number} r0
             * @param {number} r1
             * @param {number} pct
             * @return {number}
             */
            var logRangeFraction = function logRangeFraction(r0, r1, pct) {
                // Computing the inverse of toPercentXCoord. The function was arrived at with
                // the following steps:
                //
                // Original calcuation:
                // pct = (log(x) - log(xRange[0])) / (log(xRange[1]) - log(xRange[0])));
                //
                // Multiply both sides by the right-side denominator.
                // pct * (log(xRange[1] - log(xRange[0]))) = log(x) - log(xRange[0])
                //
                // add log(xRange[0]) to both sides
                // log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])) = log(x);
                //
                // Swap both sides of the equation,
                // log(x) = log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0]))
                //
                // Use both sides as the exponent in 10^exp and we're done.
                // x = 10 ^ (log(xRange[0]) + (pct * (log(xRange[1]) - log(xRange[0])))

                var logr0 = log10(r0);
                var logr1 = log10(r1);
                var exponent = logr0 + pct * (logr1 - logr0);
                var value = Math.pow(LOG_SCALE, exponent);
                return value;
            };

            exports.logRangeFraction = logRangeFraction;
            /** A dotted line stroke pattern. */
            var DOTTED_LINE = [2, 2];
            exports.DOTTED_LINE = DOTTED_LINE;
            /** A dashed line stroke pattern. */
            var DASHED_LINE = [7, 3];
            exports.DASHED_LINE = DASHED_LINE;
            /** A dot dash stroke pattern. */
            var DOT_DASH_LINE = [7, 2, 2, 2];

            exports.DOT_DASH_LINE = DOT_DASH_LINE;
            // Directions for panning and zooming. Use bit operations when combined
            // values are possible.
            var HORIZONTAL = 1;
            exports.HORIZONTAL = HORIZONTAL;
            var VERTICAL = 2;

            exports.VERTICAL = VERTICAL;
            /**
             * Return the 2d context for a dygraph canvas.
             *
             * This method is only exposed for the sake of replacing the function in
             * automated tests.
             *
             * @param {!HTMLCanvasElement} canvas
             * @return {!CanvasRenderingContext2D}
             * @private
             */
            var getContext = function getContext(canvas) {
                return (/** @type{!CanvasRenderingContext2D}*/canvas.getContext("2d")
                );
            };

            exports.getContext = getContext;
            /**
             * Add an event handler.
             * @param {!Node} elem The element to add the event to.
             * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
             * @param {function(Event):(boolean|undefined)} fn The function to call
             *     on the event. The function takes one parameter: the event object.
             * @private
             */
            var addEvent = function addEvent(elem, type, fn) {
                elem.addEventListener(type, fn, false);
            };

            exports.addEvent = addEvent;
            /**
             * Remove an event handler.
             * @param {!Node} elem The element to remove the event from.
             * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
             * @param {function(Event):(boolean|undefined)} fn The function to call
             *     on the event. The function takes one parameter: the event object.
             */

            function removeEvent(elem, type, fn) {
                elem.removeEventListener(type, fn, false);
            }

            ;

            /**
             * Cancels further processing of an event. This is useful to prevent default
             * browser actions, e.g. highlighting text on a double-click.
             * Based on the article at
             * http://www.switchonthecode.com/tutorials/javascript-tutorial-the-scroll-wheel
             * @param {!Event} e The event whose normal behavior should be canceled.
             * @private
             */

            function cancelEvent(e) {
                e = e ? e : window.event;
                if (e.stopPropagation) {
                    e.stopPropagation();
                }
                if (e.preventDefault) {
                    e.preventDefault();
                }
                e.cancelBubble = true;
                e.cancel = true;
                e.returnValue = false;
                return false;
            }

            ;

            /**
             * Convert hsv values to an rgb(r,g,b) string. Taken from MochiKit.Color. This
             * is used to generate default series colors which are evenly spaced on the
             * color wheel.
             * @param { number } hue Range is 0.0-1.0.
             * @param { number } saturation Range is 0.0-1.0.
             * @param { number } value Range is 0.0-1.0.
             * @return { string } "rgb(r,g,b)" where r, g and b range from 0-255.
             * @private
             */

            function hsvToRGB(hue, saturation, value) {
                var red;
                var green;
                var blue;
                if (saturation === 0) {
                    red = value;
                    green = value;
                    blue = value;
                } else {
                    var i = Math.floor(hue * 6);
                    var f = hue * 6 - i;
                    var p = value * (1 - saturation);
                    var q = value * (1 - saturation * f);
                    var t = value * (1 - saturation * (1 - f));
                    switch (i) {
                        case 1:
                            red = q; green = value; blue = p; break;
                        case 2:
                            red = p; green = value; blue = t; break;
                        case 3:
                            red = p; green = q; blue = value; break;
                        case 4:
                            red = t; green = p; blue = value; break;
                        case 5:
                            red = value; green = p; blue = q; break;
                        case 6: // fall through
                        case 0:
                            red = value; green = t; blue = p; break;
                    }
                }
                red = Math.floor(255 * red + 0.5);
                green = Math.floor(255 * green + 0.5);
                blue = Math.floor(255 * blue + 0.5);
                return 'rgb(' + red + ',' + green + ',' + blue + ')';
            }

            ;

            /**
             * Find the coordinates of an object relative to the top left of the page.
             *
             * @param {Node} obj
             * @return {{x:number,y:number}}
             * @private
             */

            function findPos(obj) {
                var p = obj.getBoundingClientRect(),
                    w = window,
                    d = document.documentElement;

                return {
                    x: p.left + (w.pageXOffset || d.scrollLeft),
                    y: p.top + (w.pageYOffset || d.scrollTop)
                };
            }

            ;

            /**
             * Returns the x-coordinate of the event in a coordinate system where the
             * top-left corner of the page (not the window) is (0,0).
             * Taken from MochiKit.Signal
             * @param {!Event} e
             * @return {number}
             * @private
             */

            function pageX(e) {
                return !e.pageX || e.pageX < 0 ? 0 : e.pageX;
            }

            ;

            /**
             * Returns the y-coordinate of the event in a coordinate system where the
             * top-left corner of the page (not the window) is (0,0).
             * Taken from MochiKit.Signal
             * @param {!Event} e
             * @return {number}
             * @private
             */

            function pageY(e) {
                return !e.pageY || e.pageY < 0 ? 0 : e.pageY;
            }

            ;

            /**
             * Converts page the x-coordinate of the event to pixel x-coordinates on the
             * canvas (i.e. DOM Coords).
             * @param {!Event} e Drag event.
             * @param {!DygraphInteractionContext} context Interaction context object.
             * @return {number} The amount by which the drag has moved to the right.
             */

            function dragGetX_(e, context) {
                return pageX(e) - context.px;
            }

            ;

            /**
             * Converts page the y-coordinate of the event to pixel y-coordinates on the
             * canvas (i.e. DOM Coords).
             * @param {!Event} e Drag event.
             * @param {!DygraphInteractionContext} context Interaction context object.
             * @return {number} The amount by which the drag has moved down.
             */

            function dragGetY_(e, context) {
                return pageY(e) - context.py;
            }

            ;

            /**
             * This returns true unless the parameter is 0, null, undefined or NaN.
             * TODO(danvk): rename this function to something like 'isNonZeroNan'.
             *
             * @param {number} x The number to consider.
             * @return {boolean} Whether the number is zero or NaN.
             * @private
             */

            function isOK(x) {
                return !!x && !isNaN(x);
            }

            ;

            /**
             * @param {{x:?number,y:?number,yval:?number}} p The point to consider, valid
             *     points are {x, y} objects
             * @param {boolean=} opt_allowNaNY Treat point with y=NaN as valid
             * @return {boolean} Whether the point has numeric x and y.
             * @private
             */

            function isValidPoint(p, opt_allowNaNY) {
                if (!p) return false; // null or undefined object
                if (p.yval === null) return false; // missing point
                if (p.x === null || p.x === undefined) return false;
                if (p.y === null || p.y === undefined) return false;
                if (isNaN(p.x) || !opt_allowNaNY && isNaN(p.y)) return false;
                return true;
            }

            ;

            /**
             * Number formatting function which mimics the behavior of %g in printf, i.e.
             * either exponential or fixed format (without trailing 0s) is used depending on
             * the length of the generated string.  The advantage of this format is that
             * there is a predictable upper bound on the resulting string length,
             * significant figures are not dropped, and normal numbers are not displayed in
             * exponential notation.
             *
             * NOTE: JavaScript's native toPrecision() is NOT a drop-in replacement for %g.
             * It creates strings which are too long for absolute values between 10^-4 and
             * 10^-6, e.g. '0.00001' instead of '1e-5'. See tests/number-format.html for
             * output examples.
             *
             * @param {number} x The number to format
             * @param {number=} opt_precision The precision to use, default 2.
             * @return {string} A string formatted like %g in printf.  The max generated
             *                  string length should be precision + 6 (e.g 1.123e+300).
             */

            function floatFormat(x, opt_precision) {
                // Avoid invalid precision values; [1, 21] is the valid range.
                var p = Math.min(Math.max(1, opt_precision || 2), 21);

                // This is deceptively simple.  The actual algorithm comes from:
                //
                // Max allowed length = p + 4
                // where 4 comes from 'e+n' and '.'.
                //
                // Length of fixed format = 2 + y + p
                // where 2 comes from '0.' and y = # of leading zeroes.
                //
                // Equating the two and solving for y yields y = 2, or 0.00xxxx which is
                // 1.0e-3.
                //
                // Since the behavior of toPrecision() is identical for larger numbers, we
                // don't have to worry about the other bound.
                //
                // Finally, the argument for toExponential() is the number of trailing digits,
                // so we take off 1 for the value before the '.'.
                return Math.abs(x) < 1.0e-3 && x !== 0.0 ? x.toExponential(p - 1) : x.toPrecision(p);
            }

            ;

            /**
             * Converts '9' to '09' (useful for dates)
             * @param {number} x
             * @return {string}
             * @private
             */

            function zeropad(x) {
                if (x < 10) return "0" + x; else return "" + x;
            }

            ;

            /**
             * Date accessors to get the parts of a calendar date (year, month,
             * day, hour, minute, second and millisecond) according to local time,
             * and factory method to call the Date constructor with an array of arguments.
             */
            var DateAccessorsLocal = {
                getFullYear: function getFullYear(d) {
                    return d.getFullYear();
                },
                getMonth: function getMonth(d) {
                    return d.getMonth();
                },
                getDate: function getDate(d) {
                    return d.getDate();
                },
                getHours: function getHours(d) {
                    return d.getHours();
                },
                getMinutes: function getMinutes(d) {
                    return d.getMinutes();
                },
                getSeconds: function getSeconds(d) {
                    return d.getSeconds();
                },
                getMilliseconds: function getMilliseconds(d) {
                    return d.getMilliseconds();
                },
                getDay: function getDay(d) {
                    return d.getDay();
                },
                makeDate: function makeDate(y, m, d, hh, mm, ss, ms) {
                    return new Date(y, m, d, hh, mm, ss, ms);
                }
            };

            exports.DateAccessorsLocal = DateAccessorsLocal;
            /**
             * Date accessors to get the parts of a calendar date (year, month,
             * day of month, hour, minute, second and millisecond) according to UTC time,
             * and factory method to call the Date constructor with an array of arguments.
             */
            var DateAccessorsUTC = {
                getFullYear: function getFullYear(d) {
                    return d.getUTCFullYear();
                },
                getMonth: function getMonth(d) {
                    return d.getUTCMonth();
                },
                getDate: function getDate(d) {
                    return d.getUTCDate();
                },
                getHours: function getHours(d) {
                    return d.getUTCHours();
                },
                getMinutes: function getMinutes(d) {
                    return d.getUTCMinutes();
                },
                getSeconds: function getSeconds(d) {
                    return d.getUTCSeconds();
                },
                getMilliseconds: function getMilliseconds(d) {
                    return d.getUTCMilliseconds();
                },
                getDay: function getDay(d) {
                    return d.getUTCDay();
                },
                makeDate: function makeDate(y, m, d, hh, mm, ss, ms) {
                    return new Date(Date.UTC(y, m, d, hh, mm, ss, ms));
                }
            };

            exports.DateAccessorsUTC = DateAccessorsUTC;
            /**
             * Return a string version of the hours, minutes and seconds portion of a date.
             * @param {number} hh The hours (from 0-23)
             * @param {number} mm The minutes (from 0-59)
             * @param {number} ss The seconds (from 0-59)
             * @return {string} A time of the form "HH:MM" or "HH:MM:SS"
             * @private
             */

            function hmsString_(hh, mm, ss, ms) {
                var ret = zeropad(hh) + ":" + zeropad(mm);
                if (ss) {
                    ret += ":" + zeropad(ss);
                    if (ms) {
                        var str = "" + ms;
                        ret += "." + ('000' + str).substring(str.length);
                    }
                }
                return ret;
            }

            ;

            /**
             * Convert a JS date (millis since epoch) to a formatted string.
             * @param {number} time The JavaScript time value (ms since epoch)
             * @param {boolean} utc Whether output UTC or local time
             * @return {string} A date of one of these forms:
             *     "YYYY/MM/DD", "YYYY/MM/DD HH:MM" or "YYYY/MM/DD HH:MM:SS"
             * @private
             */

            function dateString_(time, utc) {
                var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;
                var date = new Date(time);
                var y = accessors.getFullYear(date);
                var m = accessors.getMonth(date);
                var d = accessors.getDate(date);
                var hh = accessors.getHours(date);
                var mm = accessors.getMinutes(date);
                var ss = accessors.getSeconds(date);
                var ms = accessors.getMilliseconds(date);
                // Get a year string:
                var year = "" + y;
                // Get a 0 padded month string
                var month = zeropad(m + 1); //months are 0-offset, sigh
                // Get a 0 padded day string
                var day = zeropad(d);
                var frac = hh * 3600 + mm * 60 + ss + 1e-3 * ms;
                var ret = year + "/" + month + "/" + day;
                if (frac) {
                    ret += " " + hmsString_(hh, mm, ss, ms);
                }
                return ret;
            }

            ;

            /**
             * Round a number to the specified number of digits past the decimal point.
             * @param {number} num The number to round
             * @param {number} places The number of decimals to which to round
             * @return {number} The rounded number
             * @private
             */

            function round_(num, places) {
                var shift = Math.pow(10, places);
                return Math.round(num * shift) / shift;
            }

            ;

            /**
             * Implementation of binary search over an array.
             * Currently does not work when val is outside the range of arry's values.
             * @param {number} val the value to search for
             * @param {Array.<number>} arry is the value over which to search
             * @param {number} abs If abs > 0, find the lowest entry greater than val
             *     If abs < 0, find the highest entry less than val.
             *     If abs == 0, find the entry that equals val.
             * @param {number=} low The first index in arry to consider (optional)
             * @param {number=} high The last index in arry to consider (optional)
             * @return {number} Index of the element, or -1 if it isn't found.
             * @private
             */

            function binarySearch(_x, _x2, _x3, _x4, _x5) {
                var _again = true;

                _function: while (_again) {
                    var val = _x,
                        arry = _x2,
                        abs = _x3,
                        low = _x4,
                        high = _x5;
                    _again = false;

                    if (low === null || low === undefined || high === null || high === undefined) {
                        low = 0;
                        high = arry.length - 1;
                    }
                    if (low > high) {
                        return -1;
                    }
                    if (abs === null || abs === undefined) {
                        abs = 0;
                    }
                    var validIndex = function validIndex(idx) {
                        return idx >= 0 && idx < arry.length;
                    };
                    var mid = parseInt((low + high) / 2, 10);
                    var element = arry[mid];
                    var idx;
                    if (element == val) {
                        return mid;
                    } else if (element > val) {
                        if (abs > 0) {
                            // Accept if element > val, but also if prior element < val.
                            idx = mid - 1;
                            if (validIndex(idx) && arry[idx] < val) {
                                return mid;
                            }
                        }
                        _x = val;
                        _x2 = arry;
                        _x3 = abs;
                        _x4 = low;
                        _x5 = mid - 1;
                        _again = true;
                        validIndex = mid = element = idx = undefined;
                        continue _function;
                    } else if (element < val) {
                        if (abs < 0) {
                            // Accept if element < val, but also if prior element > val.
                            idx = mid + 1;
                            if (validIndex(idx) && arry[idx] > val) {
                                return mid;
                            }
                        }
                        _x = val;
                        _x2 = arry;
                        _x3 = abs;
                        _x4 = mid + 1;
                        _x5 = high;
                        _again = true;
                        validIndex = mid = element = idx = undefined;
                        continue _function;
                    }
                    return -1; // can't actually happen, but makes closure compiler happy
                }
            }

            ;

            /**
             * Parses a date, returning the number of milliseconds since epoch. This can be
             * passed in as an xValueParser in the Dygraph constructor.
             * TODO(danvk): enumerate formats that this understands.
             *
             * @param {string} dateStr A date in a variety of possible string formats.
             * @return {number} Milliseconds since epoch.
             * @private
             */

            function dateParser(dateStr) {
                var dateStrSlashed;
                var d;

                // Let the system try the format first, with one caveat:
                // YYYY-MM-DD[ HH:MM:SS] is interpreted as UTC by a variety of browsers.
                // dygraphs displays dates in local time, so this will result in surprising
                // inconsistencies. But if you specify "T" or "Z" (i.e. YYYY-MM-DDTHH:MM:SS),
                // then you probably know what you're doing, so we'll let you go ahead.
                // Issue: http://code.google.com/p/dygraphs/issues/detail?id=255
                if (dateStr.search("-") == -1 || dateStr.search("T") != -1 || dateStr.search("Z") != -1) {
                    d = dateStrToMillis(dateStr);
                    if (d && !isNaN(d)) return d;
                }

                if (dateStr.search("-") != -1) {
                    // e.g. '2009-7-12' or '2009-07-12'
                    dateStrSlashed = dateStr.replace("-", "/", "g");
                    while (dateStrSlashed.search("-") != -1) {
                        dateStrSlashed = dateStrSlashed.replace("-", "/");
                    }
                    d = dateStrToMillis(dateStrSlashed);
                } else if (dateStr.length == 8) {
                    // e.g. '20090712'
                    // TODO(danvk): remove support for this format. It's confusing.
                    dateStrSlashed = dateStr.substr(0, 4) + "/" + dateStr.substr(4, 2) + "/" + dateStr.substr(6, 2);
                    d = dateStrToMillis(dateStrSlashed);
                } else {
                    // Any format that Date.parse will accept, e.g. "2009/07/12" or
                    // "2009/07/12 12:34:56"
                    d = dateStrToMillis(dateStr);
                }

                if (!d || isNaN(d)) {
                    console.error("Couldn't parse " + dateStr + " as a date");
                }
                return d;
            }

            ;

            /**
             * This is identical to JavaScript's built-in Date.parse() method, except that
             * it doesn't get replaced with an incompatible method by aggressive JS
             * libraries like MooTools or Joomla.
             * @param {string} str The date string, e.g. "2011/05/06"
             * @return {number} millis since epoch
             * @private
             */

            function dateStrToMillis(str) {
                return new Date(str).getTime();
            }

            ;

            // These functions are all based on MochiKit.
            /**
             * Copies all the properties from o to self.
             *
             * @param {!Object} self
             * @param {!Object} o
             * @return {!Object}
             */

            function update(self, o) {
                if (typeof o != 'undefined' && o !== null) {
                    for (var k in o) {
                        if (o.hasOwnProperty(k)) {
                            self[k] = o[k];
                        }
                    }
                }
                return self;
            }

            ;

            /**
             * Copies all the properties from o to self.
             *
             * @param {!Object} self
             * @param {!Object} o
             * @return {!Object}
             * @private
             */

            function updateDeep(self, o) {
                // Taken from http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
                function isNode(o) {
                    return typeof Node === "object" ? o instanceof Node : typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string";
                }

                if (typeof o != 'undefined' && o !== null) {
                    for (var k in o) {
                        if (o.hasOwnProperty(k)) {
                            if (o[k] === null) {
                                self[k] = null;
                            } else if (isArrayLike(o[k])) {
                                self[k] = o[k].slice();
                            } else if (isNode(o[k])) {
                                // DOM objects are shallowly-copied.
                                self[k] = o[k];
                            } else if (typeof o[k] == 'object') {
                                if (typeof self[k] != 'object' || self[k] === null) {
                                    self[k] = {};
                                }
                                updateDeep(self[k], o[k]);
                            } else {
                                self[k] = o[k];
                            }
                        }
                    }
                }
                return self;
            }

            ;

            /**
             * @param {*} o
             * @return {boolean}
             * @private
             */

            function isArrayLike(o) {
                var typ = typeof o;
                if (typ != 'object' && !(typ == 'function' && typeof o.item == 'function') || o === null || typeof o.length != 'number' || o.nodeType === 3) {
                    return false;
                }
                return true;
            }

            ;

            /**
             * @param {Object} o
             * @return {boolean}
             * @private
             */

            function isDateLike(o) {
                if (typeof o != "object" || o === null || typeof o.getTime != 'function') {
                    return false;
                }
                return true;
            }

            ;

            /**
             * Note: this only seems to work for arrays.
             * @param {!Array} o
             * @return {!Array}
             * @private
             */

            function clone(o) {
                // TODO(danvk): figure out how MochiKit's version works
                var r = [];
                for (var i = 0; i < o.length; i++) {
                    if (isArrayLike(o[i])) {
                        r.push(clone(o[i]));
                    } else {
                        r.push(o[i]);
                    }
                }
                return r;
            }

            ;

            /**
             * Create a new canvas element.
             *
             * @return {!HTMLCanvasElement}
             * @private
             */

            function createCanvas() {
                return document.createElement('canvas');
            }

            ;

            /**
             * Returns the context's pixel ratio, which is the ratio between the device
             * pixel ratio and the backing store ratio. Typically this is 1 for conventional
             * displays, and > 1 for HiDPI displays (such as the Retina MBP).
             * See http://www.html5rocks.com/en/tutorials/canvas/hidpi/ for more details.
             *
             * @param {!CanvasRenderingContext2D} context The canvas's 2d context.
             * @return {number} The ratio of the device pixel ratio and the backing store
             * ratio for the specified context.
             */

            function getContextPixelRatio(context) {
                try {
                    var devicePixelRatio = window.devicePixelRatio;
                    var backingStoreRatio = context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
                    if (devicePixelRatio !== undefined) {
                        return devicePixelRatio / backingStoreRatio;
                    } else {
                        // At least devicePixelRatio must be defined for this ratio to make sense.
                        // We default backingStoreRatio to 1: this does not exist on some browsers
                        // (i.e. desktop Chrome).
                        return 1;
                    }
                } catch (e) {
                    return 1;
                }
            }

            ;

            /**
             * TODO(danvk): use @template here when it's better supported for classes.
             * @param {!Array} array
             * @param {number} start
             * @param {number} length
             * @param {function(!Array,?):boolean=} predicate
             * @constructor
             */

            function Iterator(array, start, length, predicate) {
                start = start || 0;
                length = length || array.length;
                this.hasNext = true; // Use to identify if there's another element.
                this.peek = null; // Use for look-ahead
                this.start_ = start;
                this.array_ = array;
                this.predicate_ = predicate;
                this.end_ = Math.min(array.length, start + length);
                this.nextIdx_ = start - 1; // use -1 so initial advance works.
                this.next(); // ignoring result.
            }

            ;

            /**
             * @return {Object}
             */
            Iterator.prototype.next = function () {
                if (!this.hasNext) {
                    return null;
                }
                var obj = this.peek;

                var nextIdx = this.nextIdx_ + 1;
                var found = false;
                while (nextIdx < this.end_) {
                    if (!this.predicate_ || this.predicate_(this.array_, nextIdx)) {
                        this.peek = this.array_[nextIdx];
                        found = true;
                        break;
                    }
                    nextIdx++;
                }
                this.nextIdx_ = nextIdx;
                if (!found) {
                    this.hasNext = false;
                    this.peek = null;
                }
                return obj;
            };

            /**
             * Returns a new iterator over array, between indexes start and
             * start + length, and only returns entries that pass the accept function
             *
             * @param {!Array} array the array to iterate over.
             * @param {number} start the first index to iterate over, 0 if absent.
             * @param {number} length the number of elements in the array to iterate over.
             *     This, along with start, defines a slice of the array, and so length
             *     doesn't imply the number of elements in the iterator when accept doesn't
             *     always accept all values. array.length when absent.
             * @param {function(?):boolean=} opt_predicate a function that takes
             *     parameters array and idx, which returns true when the element should be
             *     returned.  If omitted, all elements are accepted.
             * @private
             */

            function createIterator(array, start, length, opt_predicate) {
                return new Iterator(array, start, length, opt_predicate);
            }

            ;

            // Shim layer with setTimeout fallback.
            // From: http://paulirish.com/2011/requestanimationframe-for-smart-animating/
            // Should be called with the window context:
            //   Dygraph.requestAnimFrame.call(window, function() {})
            var requestAnimFrame = (function () {
                return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) {
                    window.setTimeout(callback, 1000 / 60);
                };
            })();

            exports.requestAnimFrame = requestAnimFrame;
            /**
             * Call a function at most maxFrames times at an attempted interval of
             * framePeriodInMillis, then call a cleanup function once. repeatFn is called
             * once immediately, then at most (maxFrames - 1) times asynchronously. If
             * maxFrames==1, then cleanup_fn() is also called synchronously.  This function
             * is used to sequence animation.
             * @param {function(number)} repeatFn Called repeatedly -- takes the frame
             *     number (from 0 to maxFrames-1) as an argument.
             * @param {number} maxFrames The max number of times to call repeatFn
             * @param {number} framePeriodInMillis Max requested time between frames.
             * @param {function()} cleanupFn A function to call after all repeatFn calls.
             * @private
             */

            function repeatAndCleanup(repeatFn, maxFrames, framePeriodInMillis, cleanupFn) {
                var frameNumber = 0;
                var previousFrameNumber;
                var startTime = new Date().getTime();
                repeatFn(frameNumber);
                if (maxFrames == 1) {
                    cleanupFn();
                    return;
                }
                var maxFrameArg = maxFrames - 1;

                (function loop() {
                    if (frameNumber >= maxFrames) return;
                    requestAnimFrame.call(window, function () {
                        // Determine which frame to draw based on the delay so far.  Will skip
                        // frames if necessary.
                        var currentTime = new Date().getTime();
                        var delayInMillis = currentTime - startTime;
                        previousFrameNumber = frameNumber;
                        frameNumber = Math.floor(delayInMillis / framePeriodInMillis);
                        var frameDelta = frameNumber - previousFrameNumber;
                        // If we predict that the subsequent repeatFn call will overshoot our
                        // total frame target, so our last call will cause a stutter, then jump to
                        // the last call immediately.  If we're going to cause a stutter, better
                        // to do it faster than slower.
                        var predictOvershootStutter = frameNumber + frameDelta > maxFrameArg;
                        if (predictOvershootStutter || frameNumber >= maxFrameArg) {
                            repeatFn(maxFrameArg); // Ensure final call with maxFrameArg.
                            cleanupFn();
                        } else {
                            if (frameDelta !== 0) {
                                // Don't call repeatFn with duplicate frames.
                                repeatFn(frameNumber);
                            }
                            loop();
                        }
                    });
                })();
            }

            ;

            // A whitelist of options that do not change pixel positions.
            var pixelSafeOptions = {
                'annotationClickHandler': true,
                'annotationDblClickHandler': true,
                'annotationMouseOutHandler': true,
                'annotationMouseOverHandler': true,
                'axisLineColor': true,
                'axisLineWidth': true,
                'clickCallback': true,
                'drawCallback': true,
                'drawHighlightPointCallback': true,
                'drawPoints': true,
                'drawPointCallback': true,
                'drawGrid': true,
                'fillAlpha': true,
                'gridLineColor': true,
                'gridLineWidth': true,
                'hideOverlayOnMouseOut': true,
                'highlightCallback': true,
                'highlightCircleSize': true,
                'interactionModel': true,
                'labelsDiv': true,
                'labelsKMB': true,
                'labelsKMG2': true,
                'labelsSeparateLines': true,
                'labelsShowZeroValues': true,
                'legend': true,
                'panEdgeFraction': true,
                'pixelsPerYLabel': true,
                'pointClickCallback': true,
                'pointSize': true,
                'rangeSelectorPlotFillColor': true,
                'rangeSelectorPlotFillGradientColor': true,
                'rangeSelectorPlotStrokeColor': true,
                'rangeSelectorBackgroundStrokeColor': true,
                'rangeSelectorBackgroundLineWidth': true,
                'rangeSelectorPlotLineWidth': true,
                'rangeSelectorForegroundStrokeColor': true,
                'rangeSelectorForegroundLineWidth': true,
                'rangeSelectorAlpha': true,
                'showLabelsOnHighlight': true,
                'showRoller': true,
                'strokeWidth': true,
                'underlayCallback': true,
                'unhighlightCallback': true,
                'zoomCallback': true
            };

            /**
             * This function will scan the option list and determine if they
             * require us to recalculate the pixel positions of each point.
             * TODO: move this into dygraph-options.js
             * @param {!Array.<string>} labels a list of options to check.
             * @param {!Object} attrs
             * @return {boolean} true if the graph needs new points else false.
             * @private
             */

            function isPixelChangingOptionList(labels, attrs) {
                // Assume that we do not require new points.
                // This will change to true if we actually do need new points.

                // Create a dictionary of series names for faster lookup.
                // If there are no labels, then the dictionary stays empty.
                var seriesNamesDictionary = {};
                if (labels) {
                    for (var i = 1; i < labels.length; i++) {
                        seriesNamesDictionary[labels[i]] = true;
                    }
                }

                // Scan through a flat (i.e. non-nested) object of options.
                // Returns true/false depending on whether new points are needed.
                var scanFlatOptions = function scanFlatOptions(options) {
                    for (var property in options) {
                        if (options.hasOwnProperty(property) && !pixelSafeOptions[property]) {
                            return true;
                        }
                    }
                    return false;
                };

                // Iterate through the list of updated options.
                for (var property in attrs) {
                    if (!attrs.hasOwnProperty(property)) continue;

                    // Find out of this field is actually a series specific options list.
                    if (property == 'highlightSeriesOpts' || seriesNamesDictionary[property] && !attrs.series) {
                        // This property value is a list of options for this series.
                        if (scanFlatOptions(attrs[property])) return true;
                    } else if (property == 'series' || property == 'axes') {
                        // This is twice-nested options list.
                        var perSeries = attrs[property];
                        for (var series in perSeries) {
                            if (perSeries.hasOwnProperty(series) && scanFlatOptions(perSeries[series])) {
                                return true;
                            }
                        }
                    } else {
                        // If this was not a series specific option list, check if it's a pixel
                        // changing property.
                        if (!pixelSafeOptions[property]) return true;
                    }
                }

                return false;
            }

            ;

            var Circles = {
                DEFAULT: function DEFAULT(g, name, ctx, canvasx, canvasy, color, radius) {
                    ctx.beginPath();
                    ctx.fillStyle = color;
                    ctx.arc(canvasx, canvasy, radius, 0, 2 * Math.PI, false);
                    ctx.fill();
                }
                // For more shapes, include extras/shapes.js
            };

            exports.Circles = Circles;
            /**
             * Determine whether |data| is delimited by CR, CRLF, LF, LFCR.
             * @param {string} data
             * @return {?string} the delimiter that was detected (or null on failure).
             */

            function detectLineDelimiter(data) {
                for (var i = 0; i < data.length; i++) {
                    var code = data.charAt(i);
                    if (code === '\r') {
                        // Might actually be "\r\n".
                        if (i + 1 < data.length && data.charAt(i + 1) === '\n') {
                            return '\r\n';
                        }
                        return code;
                    }
                    if (code === '\n') {
                        // Might actually be "\n\r".
                        if (i + 1 < data.length && data.charAt(i + 1) === '\r') {
                            return '\n\r';
                        }
                        return code;
                    }
                }

                return null;
            }

            ;

            /**
             * Is one node contained by another?
             * @param {Node} containee The contained node.
             * @param {Node} container The container node.
             * @return {boolean} Whether containee is inside (or equal to) container.
             * @private
             */

            function isNodeContainedBy(containee, container) {
                if (container === null || containee === null) {
                    return false;
                }
                var containeeNode = /** @type {Node} */containee;
                while (containeeNode && containeeNode !== container) {
                    containeeNode = containeeNode.parentNode;
                }
                return containeeNode === container;
            }

            ;

            // This masks some numeric issues in older versions of Firefox,
            // where 1.0/Math.pow(10,2) != Math.pow(10,-2).
            /** @type {function(number,number):number} */

            function pow(base, exp) {
                if (exp < 0) {
                    return 1.0 / Math.pow(base, -exp);
                }
                return Math.pow(base, exp);
            }

            ;

            var RGBA_RE = /^rgba?\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})(?:,\s*([01](?:\.\d+)?))?\)$/;

            /**
             * Helper for toRGB_ which parses strings of the form:
             * rgb(123, 45, 67)
             * rgba(123, 45, 67, 0.5)
             * @return parsed {r,g,b,a?} tuple or null.
             */
            function parseRGBA(rgbStr) {
                var bits = RGBA_RE.exec(rgbStr);
                if (!bits) return null;
                var r = parseInt(bits[1], 10),
                    g = parseInt(bits[2], 10),
                    b = parseInt(bits[3], 10);
                if (bits[4]) {
                    return { r: r, g: g, b: b, a: parseFloat(bits[4]) };
                } else {
                    return { r: r, g: g, b: b };
                }
            }

            /**
             * Converts any valid CSS color (hex, rgb(), named color) to an RGB tuple.
             *
             * @param {!string} colorStr Any valid CSS color string.
             * @return {{r:number,g:number,b:number,a:number?}} Parsed RGB tuple.
             * @private
             */

            function toRGB_(colorStr) {
                // Strategy: First try to parse colorStr directly. This is fast & avoids DOM
                // manipulation.  If that fails (e.g. for named colors like 'red'), then
                // create a hidden DOM element and parse its computed color.
                var rgb = parseRGBA(colorStr);
                if (rgb) return rgb;

                var div = document.createElement('div');
                div.style.backgroundColor = colorStr;
                div.style.visibility = 'hidden';
                document.body.appendChild(div);
                var rgbStr = window.getComputedStyle(div, null).backgroundColor;
                document.body.removeChild(div);
                return parseRGBA(rgbStr);
            }

            ;

            /**
             * Checks whether the browser supports the &lt;canvas&gt; tag.
             * @param {HTMLCanvasElement=} opt_canvasElement Pass a canvas element as an
             *     optimization if you have one.
             * @return {boolean} Whether the browser supports canvas.
             */

            function isCanvasSupported(opt_canvasElement) {
                try {
                    var canvas = opt_canvasElement || document.createElement("canvas");
                    canvas.getContext("2d");
                } catch (e) {
                    return false;
                }
                return true;
            }

            ;

            /**
             * Parses the value as a floating point number. This is like the parseFloat()
             * built-in, but with a few differences:
             * - the empty string is parsed as null, rather than NaN.
             * - if the string cannot be parsed at all, an error is logged.
             * If the string can't be parsed, this method returns null.
             * @param {string} x The string to be parsed
             * @param {number=} opt_line_no The line number from which the string comes.
             * @param {string=} opt_line The text of the line from which the string comes.
             */

            function parseFloat_(x, opt_line_no, opt_line) {
                var val = parseFloat(x);
                if (!isNaN(val)) return val;

                // Try to figure out what happeend.
                // If the value is the empty string, parse it as null.
                if (/^ *$/.test(x)) return null;

                // If it was actually "NaN", return it as NaN.
                if (/^ *nan *$/i.test(x)) return NaN;

                // Looks like a parsing error.
                var msg = "Unable to parse '" + x + "' as a number";
                if (opt_line !== undefined && opt_line_no !== undefined) {
                    msg += " on line " + (1 + (opt_line_no || 0)) + " ('" + opt_line + "') of CSV.";
                }
                console.error(msg);

                return null;
            }

            ;

            // Label constants for the labelsKMB and labelsKMG2 options.
            // (i.e. '100000' -> '100K')
            var KMB_LABELS = ['K', 'M', 'B', 'T', 'Q'];
            var KMG2_BIG_LABELS = ['k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'];
            var KMG2_SMALL_LABELS = ['m', 'u', 'n', 'p', 'f', 'a', 'z', 'y'];

            /**
             * @private
             * Return a string version of a number. This respects the digitsAfterDecimal
             * and maxNumberWidth options.
             * @param {number} x The number to be formatted
             * @param {Dygraph} opts An options view
             */

            function numberValueFormatter(x, opts) {
                var sigFigs = opts('sigFigs');

                if (sigFigs !== null) {
                    // User has opted for a fixed number of significant figures.
                    return floatFormat(x, sigFigs);
                }

                var digits = opts('digitsAfterDecimal');
                var maxNumberWidth = opts('maxNumberWidth');

                var kmb = opts('labelsKMB');
                var kmg2 = opts('labelsKMG2');

                var label;

                // switch to scientific notation if we underflow or overflow fixed display.
                if (x !== 0.0 && (Math.abs(x) >= Math.pow(10, maxNumberWidth) || Math.abs(x) < Math.pow(10, -digits))) {
                    label = x.toExponential(digits);
                } else {
                    label = '' + round_(x, digits);
                }

                if (kmb || kmg2) {
                    var k;
                    var k_labels = [];
                    var m_labels = [];
                    if (kmb) {
                        k = 1000;
                        k_labels = KMB_LABELS;
                    }
                    if (kmg2) {
                        if (kmb) console.warn("Setting both labelsKMB and labelsKMG2. Pick one!");
                        k = 1024;
                        k_labels = KMG2_BIG_LABELS;
                        m_labels = KMG2_SMALL_LABELS;
                    }

                    var absx = Math.abs(x);
                    var n = pow(k, k_labels.length);
                    for (var j = k_labels.length - 1; j >= 0; j-- , n /= k) {
                        if (absx >= n) {
                            label = round_(x / n, digits) + k_labels[j];
                            break;
                        }
                    }
                    if (kmg2) {
                        // TODO(danvk): clean up this logic. Why so different than kmb?
                        var x_parts = String(x.toExponential()).split('e-');
                        if (x_parts.length === 2 && x_parts[1] >= 3 && x_parts[1] <= 24) {
                            if (x_parts[1] % 3 > 0) {
                                label = round_(x_parts[0] / pow(10, x_parts[1] % 3), digits);
                            } else {
                                label = Number(x_parts[0]).toFixed(2);
                            }
                            label += m_labels[Math.floor(x_parts[1] / 3) - 1];
                        }
                    }
                }

                return label;
            }

            ;

            /**
             * variant for use as an axisLabelFormatter.
             * @private
             */

            function numberAxisLabelFormatter(x, granularity, opts) {
                return numberValueFormatter.call(this, x, opts);
            }

            ;

            /**
             * @type {!Array.<string>}
             * @private
             * @constant
             */
            var SHORT_MONTH_NAMES_ = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

            /**
             * Convert a JS date to a string appropriate to display on an axis that
             * is displaying values at the stated granularity. This respects the
             * labelsUTC option.
             * @param {Date} date The date to format
             * @param {number} granularity One of the Dygraph granularity constants
             * @param {Dygraph} opts An options view
             * @return {string} The date formatted as local time
             * @private
             */

            function dateAxisLabelFormatter(date, granularity, opts) {
                var utc = opts('labelsUTC');
                var accessors = utc ? DateAccessorsUTC : DateAccessorsLocal;

                var year = accessors.getFullYear(date),
                    month = accessors.getMonth(date),
                    day = accessors.getDate(date),
                    hours = accessors.getHours(date),
                    mins = accessors.getMinutes(date),
                    secs = accessors.getSeconds(date),
                    millis = accessors.getMilliseconds(date);

                if (granularity >= DygraphTickers.Granularity.DECADAL) {
                    return '' + year;
                } else if (granularity >= DygraphTickers.Granularity.MONTHLY) {
                    return SHORT_MONTH_NAMES_[month] + '&#160;' + year;
                } else {
                    var frac = hours * 3600 + mins * 60 + secs + 1e-3 * millis;
                    if (frac === 0 || granularity >= DygraphTickers.Granularity.DAILY) {
                        // e.g. '21 Jan' (%d%b)
                        return zeropad(day) + '&#160;' + SHORT_MONTH_NAMES_[month];
                    } else if (granularity < DygraphTickers.Granularity.SECONDLY) {
                        // e.g. 40.310 (meaning 40 seconds and 310 milliseconds)
                        var str = "" + millis;
                        return zeropad(secs) + "." + ('000' + str).substring(str.length);
                    } else if (granularity > DygraphTickers.Granularity.MINUTELY) {
                        return hmsString_(hours, mins, secs, 0);
                    } else {
                        return hmsString_(hours, mins, secs, millis);
                    }
                }
            }

            ;
            // alias in case anyone is referencing the old method.
            // Dygraph.dateAxisFormatter = Dygraph.dateAxisLabelFormatter;

            /**
             * Return a string version of a JS date for a value label. This respects the
             * labelsUTC option.
             * @param {Date} date The date to be formatted
             * @param {Dygraph} opts An options view
             * @private
             */

            function dateValueFormatter(d, opts) {
                return dateString_(d, opts('labelsUTC'));
            }

            ;

        }, { "./dygraph-tickers": 16 }], 18: [function (require, module, exports) {
            (function (process) {
/**
 * @license
 * Copyright 2006 Dan Vanderkam (danvdk@gmail.com)
 * MIT-licensed (http://opensource.org/licenses/MIT)
 */ /**
 * @fileoverview Creates an interactive, zoomable graph based on a CSV file or
 * string. Dygraph can handle multiple series with or without error bars. The
 * date/value ranges will be automatically set. Dygraph uses the
 * &lt;canvas&gt; tag, so it only works in FF1.5+.
 * @author danvdk@gmail.com (Dan Vanderkam)

  Usage:
   <div id="graphdiv" style="width:800px; height:500px;"></div>
   <script type="text/javascript">
     new Dygraph(document.getElementById("graphdiv"),
                 "datafile.csv",  // CSV file with headers
                 { }); // options
   </script>

 The CSV file is of the form

   Date,SeriesA,SeriesB,SeriesC
   YYYYMMDD,A1,B1,C1
   YYYYMMDD,A2,B2,C2

 If the 'errorBars' option is set in the constructor, the input should be of
 the form
   Date,SeriesA,SeriesB,...
   YYYYMMDD,A1,sigmaA1,B1,sigmaB1,...
   YYYYMMDD,A2,sigmaA2,B2,sigmaB2,...

 If the 'fractions' option is set, the input should be of the form:

   Date,SeriesA,SeriesB,...
   YYYYMMDD,A1/B1,A2/B2,...
   YYYYMMDD,A1/B1,A2/B2,...

 And error bars will be calculated automatically using a binomial distribution.

 For further documentation and examples, see http://dygraphs.com/
 */'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _dygraphLayout = require('./dygraph-layout'); var _dygraphLayout2 = _interopRequireDefault(_dygraphLayout); var _dygraphCanvas = require('./dygraph-canvas'); var _dygraphCanvas2 = _interopRequireDefault(_dygraphCanvas); var _dygraphOptions = require('./dygraph-options'); var _dygraphOptions2 = _interopRequireDefault(_dygraphOptions); var _dygraphInteractionModel = require('./dygraph-interaction-model'); var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel); var _dygraphTickers = require('./dygraph-tickers'); var DygraphTickers = _interopRequireWildcard(_dygraphTickers); var _dygraphUtils = require('./dygraph-utils'); var utils = _interopRequireWildcard(_dygraphUtils); var _dygraphDefaultAttrs = require('./dygraph-default-attrs'); var _dygraphDefaultAttrs2 = _interopRequireDefault(_dygraphDefaultAttrs); var _dygraphOptionsReference = require('./dygraph-options-reference'); var _dygraphOptionsReference2 = _interopRequireDefault(_dygraphOptionsReference); var _iframeTarp = require('./iframe-tarp'); var _iframeTarp2 = _interopRequireDefault(_iframeTarp); var _datahandlerDefault = require('./datahandler/default'); var _datahandlerDefault2 = _interopRequireDefault(_datahandlerDefault); var _datahandlerBarsError = require('./datahandler/bars-error'); var _datahandlerBarsError2 = _interopRequireDefault(_datahandlerBarsError); var _datahandlerBarsCustom = require('./datahandler/bars-custom'); var _datahandlerBarsCustom2 = _interopRequireDefault(_datahandlerBarsCustom); var _datahandlerDefaultFractions = require('./datahandler/default-fractions'); var _datahandlerDefaultFractions2 = _interopRequireDefault(_datahandlerDefaultFractions); var _datahandlerBarsFractions = require('./datahandler/bars-fractions'); var _datahandlerBarsFractions2 = _interopRequireDefault(_datahandlerBarsFractions); var _datahandlerBars = require('./datahandler/bars'); var _datahandlerBars2 = _interopRequireDefault(_datahandlerBars); var _pluginsAnnotations = require('./plugins/annotations'); var _pluginsAnnotations2 = _interopRequireDefault(_pluginsAnnotations); var _pluginsAxes = require('./plugins/axes'); var _pluginsAxes2 = _interopRequireDefault(_pluginsAxes); var _pluginsChartLabels = require('./plugins/chart-labels'); var _pluginsChartLabels2 = _interopRequireDefault(_pluginsChartLabels); var _pluginsGrid = require('./plugins/grid'); var _pluginsGrid2 = _interopRequireDefault(_pluginsGrid); var _pluginsLegend = require('./plugins/legend'); var _pluginsLegend2 = _interopRequireDefault(_pluginsLegend); var _pluginsRangeSelector = require('./plugins/range-selector'); var _pluginsRangeSelector2 = _interopRequireDefault(_pluginsRangeSelector); var _dygraphGviz = require('./dygraph-gviz'); var _dygraphGviz2 = _interopRequireDefault(_dygraphGviz); "use strict"; /**
 * Creates an interactive, zoomable chart.
 *
 * @constructor
 * @param {div | String} div A div or the id of a div into which to construct
 * the chart.
 * @param {String | Function} file A file containing CSV data or a function
 * that returns this data. The most basic expected format for each line is
 * "YYYY/MM/DD,val1,val2,...". For more information, see
 * http://dygraphs.com/data.html.
 * @param {Object} attrs Various other attributes, e.g. errorBars determines
 * whether the input data contains error ranges. For a complete list of
 * options, see http://dygraphs.com/options.html.
 */var Dygraph = function Dygraph(div, data, opts) { this.__init__(div, data, opts); }; Dygraph.NAME = "Dygraph"; Dygraph.VERSION = "2.1.0"; // Various default values
                Dygraph.DEFAULT_ROLL_PERIOD = 1; Dygraph.DEFAULT_WIDTH = 480; Dygraph.DEFAULT_HEIGHT = 320; // For max 60 Hz. animation:
                Dygraph.ANIMATION_STEPS = 12; Dygraph.ANIMATION_DURATION = 200; /**
 * Standard plotters. These may be used by clients.
 * Available plotters are:
 * - Dygraph.Plotters.linePlotter: draws central lines (most common)
 * - Dygraph.Plotters.errorPlotter: draws error bars
 * - Dygraph.Plotters.fillPlotter: draws fills under lines (used with fillGraph)
 *
 * By default, the plotter is [fillPlotter, errorPlotter, linePlotter].
 * This causes all the lines to be drawn over all the fills/error bars.
 */Dygraph.Plotters = _dygraphCanvas2['default']._Plotters; // Used for initializing annotation CSS rules only once.
                Dygraph.addedAnnotationCSS = false; /**
 * Initializes the Dygraph. This creates a new DIV and constructs the PlotKit
 * and context &lt;canvas&gt; inside of it. See the constructor for details.
 * on the parameters.
 * @param {Element} div the Element to render the graph into.
 * @param {string | Function} file Source data
 * @param {Object} attrs Miscellaneous other options
 * @private
 */Dygraph.prototype.__init__ = function (div, file, attrs) {
                this.is_initial_draw_ = true; this.readyFns_ = []; // Support two-argument constructor
                    if (attrs === null || attrs === undefined) { attrs = {}; } attrs = Dygraph.copyUserAttrs_(attrs); if (typeof div == 'string') { div = document.getElementById(div); } if (!div) { throw new Error('Constructing dygraph with a non-existent div!'); } // Copy the important bits into the object
                    // TODO(danvk): most of these should just stay in the attrs_ dictionary.
                    this.maindiv_ = div; this.file_ = file; this.rollPeriod_ = attrs.rollPeriod || Dygraph.DEFAULT_ROLL_PERIOD; this.previousVerticalX_ = -1; this.fractions_ = attrs.fractions || false; this.dateWindow_ = attrs.dateWindow || null; this.annotations_ = []; // Clear the div. This ensure that, if multiple dygraphs are passed the same
                    // div, then only one will be drawn.
                    div.innerHTML = ""; // For historical reasons, the 'width' and 'height' options trump all CSS
                    // rules _except_ for an explicit 'width' or 'height' on the div.
                    // As an added convenience, if the div has zero height (like <div></div> does
                    // without any styles), then we use a default height/width.
                    if (div.style.width === '' && attrs.width) { div.style.width = attrs.width + "px"; } if (div.style.height === '' && attrs.height) { div.style.height = attrs.height + "px"; } if (div.style.height === '' && div.clientHeight === 0) { div.style.height = Dygraph.DEFAULT_HEIGHT + "px"; if (div.style.width === '') { div.style.width = Dygraph.DEFAULT_WIDTH + "px"; } } // These will be zero if the dygraph's div is hidden. In that case,
                    // use the user-specified attributes if present. If not, use zero
                    // and assume the user will call resize to fix things later.
                    this.width_ = div.clientWidth || attrs.width || 0; this.height_ = div.clientHeight || attrs.height || 0; // TODO(danvk): set fillGraph to be part of attrs_ here, not user_attrs_.
                    if (attrs.stackedGraph) {
                    attrs.fillGraph = true; // TODO(nikhilk): Add any other stackedGraph checks here.
                    } // DEPRECATION WARNING: All option processing should be moved from
                    // attrs_ and user_attrs_ to options_, which holds all this information.
                    //
                    // Dygraphs has many options, some of which interact with one another.
                    // To keep track of everything, we maintain two sets of options:
                    //
                    //  this.user_attrs_   only options explicitly set by the user.
                    //  this.attrs_        defaults, options derived from user_attrs_, data.
                    //
                    // Options are then accessed this.attr_('attr'), which first looks at
                    // user_attrs_ and then computed attrs_. This way Dygraphs can set intelligent
                    // defaults without overriding behavior that the user specifically asks for.
                    this.user_attrs_ = {}; utils.update(this.user_attrs_, attrs); // This sequence ensures that Dygraph.DEFAULT_ATTRS is never modified.
                    this.attrs_ = {}; utils.updateDeep(this.attrs_, _dygraphDefaultAttrs2['default']); this.boundaryIds_ = []; this.setIndexByName_ = {}; this.datasetIndex_ = []; this.registeredEvents_ = []; this.eventListeners_ = {}; this.attributes_ = new _dygraphOptions2['default'](this); // Create the containing DIV and other interactive elements
                    this.createInterface_(); // Activate plugins.
                    this.plugins_ = []; var plugins = Dygraph.PLUGINS.concat(this.getOption('plugins')); for (var i = 0; i < plugins.length; i++) { // the plugins option may contain either plugin classes or instances.
                        // Plugin instances contain an activate method.
                        var Plugin = plugins[i]; // either a constructor or an instance.
                        var pluginInstance; if (typeof Plugin.activate !== 'undefined') { pluginInstance = Plugin; } else { pluginInstance = new Plugin(); } var pluginDict = { plugin: pluginInstance, events: {}, options: {}, pluginOptions: {} }; var handlers = pluginInstance.activate(this); for (var eventName in handlers) {
                            if (!handlers.hasOwnProperty(eventName)) continue; // TODO(danvk): validate eventName.
                            pluginDict.events[eventName] = handlers[eventName];
                        } this.plugins_.push(pluginDict);
                    } // At this point, plugins can no longer register event handlers.
                    // Construct a map from event -> ordered list of [callback, plugin].
                    for (var i = 0; i < this.plugins_.length; i++) { var plugin_dict = this.plugins_[i]; for (var eventName in plugin_dict.events) { if (!plugin_dict.events.hasOwnProperty(eventName)) continue; var callback = plugin_dict.events[eventName]; var pair = [plugin_dict.plugin, callback]; if (!(eventName in this.eventListeners_)) { this.eventListeners_[eventName] = [pair]; } else { this.eventListeners_[eventName].push(pair); } } } this.createDragInterface_(); this.start_();
                }; /**
 * Triggers a cascade of events to the various plugins which are interested in them.
 * Returns true if the "default behavior" should be prevented, i.e. if one
 * of the event listeners called event.preventDefault().
 * @private
 */Dygraph.prototype.cascadeEvents_ = function (name, extra_props) {
                    if (!(name in this.eventListeners_)) return false; // QUESTION: can we use objects & prototypes to speed this up?
                    var e = { dygraph: this, cancelable: false, defaultPrevented: false, preventDefault: function preventDefault() { if (!e.cancelable) throw "Cannot call preventDefault on non-cancelable event."; e.defaultPrevented = true; }, propagationStopped: false, stopPropagation: function stopPropagation() { e.propagationStopped = true; } }; utils.update(e, extra_props); var callback_plugin_pairs = this.eventListeners_[name]; if (callback_plugin_pairs) { for (var i = callback_plugin_pairs.length - 1; i >= 0; i--) { var plugin = callback_plugin_pairs[i][0]; var callback = callback_plugin_pairs[i][1]; callback.call(plugin, e); if (e.propagationStopped) break; } } return e.defaultPrevented;
                }; /**
 * Fetch a plugin instance of a particular class. Only for testing.
 * @private
 * @param {!Class} type The type of the plugin.
 * @return {Object} Instance of the plugin, or null if there is none.
 */Dygraph.prototype.getPluginInstance_ = function (type) { for (var i = 0; i < this.plugins_.length; i++) { var p = this.plugins_[i]; if (p.plugin instanceof type) { return p.plugin; } } return null; }; /**
 * Returns the zoomed status of the chart for one or both axes.
 *
 * Axis is an optional parameter. Can be set to 'x' or 'y'.
 *
 * The zoomed status for an axis is set whenever a user zooms using the mouse
 * or when the dateWindow or valueRange are updated. Double-clicking or calling
 * resetZoom() resets the zoom status for the chart.
 */Dygraph.prototype.isZoomed = function (axis) { var isZoomedX = !!this.dateWindow_; if (axis === 'x') return isZoomedX; var isZoomedY = this.axes_.map(function (axis) { return !!axis.valueRange; }).indexOf(true) >= 0; if (axis === null || axis === undefined) { return isZoomedX || isZoomedY; } if (axis === 'y') return isZoomedY; throw new Error('axis parameter is [' + axis + '] must be null, \'x\' or \'y\'.'); }; /**
 * Returns information about the Dygraph object, including its containing ID.
 */Dygraph.prototype.toString = function () { var maindiv = this.maindiv_; var id = maindiv && maindiv.id ? maindiv.id : maindiv; return "[Dygraph " + id + "]"; }; /**
 * @private
 * Returns the value of an option. This may be set by the user (either in the
 * constructor or by calling updateOptions) or by dygraphs, and may be set to a
 * per-series value.
 * @param {string} name The name of the option, e.g. 'rollPeriod'.
 * @param {string} [seriesName] The name of the series to which the option
 * will be applied. If no per-series value of this option is available, then
 * the global value is returned. This is optional.
 * @return { ... } The value of the option.
 */Dygraph.prototype.attr_ = function (name, seriesName) { // For "production" code, this gets removed by uglifyjs.
                    if (typeof process !== 'undefined') {
                        if ("development" != 'production') {
                            if (typeof _dygraphOptionsReference2['default'] === 'undefined') { console.error('Must include options reference JS for testing'); } else if (!_dygraphOptionsReference2['default'].hasOwnProperty(name)) {
                                console.error('Dygraphs is using property ' + name + ', which has no ' + 'entry in the Dygraphs.OPTIONS_REFERENCE listing.'); // Only log this error once.
                                _dygraphOptionsReference2['default'][name] = true;
                            }
                        }
                    } return seriesName ? this.attributes_.getForSeries(name, seriesName) : this.attributes_.get(name);
                }; /**
 * Returns the current value for an option, as set in the constructor or via
 * updateOptions. You may pass in an (optional) series name to get per-series
 * values for the option.
 *
 * All values returned by this method should be considered immutable. If you
 * modify them, there is no guarantee that the changes will be honored or that
 * dygraphs will remain in a consistent state. If you want to modify an option,
 * use updateOptions() instead.
 *
 * @param {string} name The name of the option (e.g. 'strokeWidth')
 * @param {string=} opt_seriesName Series name to get per-series values.
 * @return {*} The value of the option.
 */Dygraph.prototype.getOption = function (name, opt_seriesName) { return this.attr_(name, opt_seriesName); }; /**
 * Like getOption(), but specifically returns a number.
 * This is a convenience function for working with the Closure Compiler.
 * @param {string} name The name of the option (e.g. 'strokeWidth')
 * @param {string=} opt_seriesName Series name to get per-series values.
 * @return {number} The value of the option.
 * @private
 */Dygraph.prototype.getNumericOption = function (name, opt_seriesName) { return (/** @type{number} */this.getOption(name, opt_seriesName)); }; /**
 * Like getOption(), but specifically returns a string.
 * This is a convenience function for working with the Closure Compiler.
 * @param {string} name The name of the option (e.g. 'strokeWidth')
 * @param {string=} opt_seriesName Series name to get per-series values.
 * @return {string} The value of the option.
 * @private
 */Dygraph.prototype.getStringOption = function (name, opt_seriesName) { return (/** @type{string} */this.getOption(name, opt_seriesName)); }; /**
 * Like getOption(), but specifically returns a boolean.
 * This is a convenience function for working with the Closure Compiler.
 * @param {string} name The name of the option (e.g. 'strokeWidth')
 * @param {string=} opt_seriesName Series name to get per-series values.
 * @return {boolean} The value of the option.
 * @private
 */Dygraph.prototype.getBooleanOption = function (name, opt_seriesName) { return (/** @type{boolean} */this.getOption(name, opt_seriesName)); }; /**
 * Like getOption(), but specifically returns a function.
 * This is a convenience function for working with the Closure Compiler.
 * @param {string} name The name of the option (e.g. 'strokeWidth')
 * @param {string=} opt_seriesName Series name to get per-series values.
 * @return {function(...)} The value of the option.
 * @private
 */Dygraph.prototype.getFunctionOption = function (name, opt_seriesName) { return (/** @type{function(...)} */this.getOption(name, opt_seriesName)); }; Dygraph.prototype.getOptionForAxis = function (name, axis) { return this.attributes_.getForAxis(name, axis); }; /**
 * @private
 * @param {string} axis The name of the axis (i.e. 'x', 'y' or 'y2')
 * @return { ... } A function mapping string -> option value
 */Dygraph.prototype.optionsViewForAxis_ = function (axis) {
                    var self = this; return function (opt) {
                        var axis_opts = self.user_attrs_.axes; if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) { return axis_opts[axis][opt]; } // I don't like that this is in a second spot.
                        if (axis === 'x' && opt === 'logscale') { // return the default value.
                            // TODO(konigsberg): pull the default from a global default.
                            return false;
                        } // user-specified attributes always trump defaults, even if they're less
                        // specific.
                        if (typeof self.user_attrs_[opt] != 'undefined') { return self.user_attrs_[opt]; } axis_opts = self.attrs_.axes; if (axis_opts && axis_opts[axis] && axis_opts[axis].hasOwnProperty(opt)) { return axis_opts[axis][opt]; } // check old-style axis options
                        // TODO(danvk): add a deprecation warning if either of these match.
                        if (axis == 'y' && self.axes_[0].hasOwnProperty(opt)) { return self.axes_[0][opt]; } else if (axis == 'y2' && self.axes_[1].hasOwnProperty(opt)) { return self.axes_[1][opt]; } return self.attr_(opt);
                    };
                }; /**
 * Returns the current rolling period, as set by the user or an option.
 * @return {number} The number of points in the rolling window
 */Dygraph.prototype.rollPeriod = function () { return this.rollPeriod_; }; /**
 * Returns the currently-visible x-range. This can be affected by zooming,
 * panning or a call to updateOptions.
 * Returns a two-element array: [left, right].
 * If the Dygraph has dates on the x-axis, these will be millis since epoch.
 */Dygraph.prototype.xAxisRange = function () { return this.dateWindow_ ? this.dateWindow_ : this.xAxisExtremes(); }; /**
 * Returns the lower- and upper-bound x-axis values of the data set.
 */Dygraph.prototype.xAxisExtremes = function () {
                    var pad = this.getNumericOption('xRangePad') / this.plotter_.area.w; if (this.numRows() === 0) { return [0 - pad, 1 + pad]; } var left = this.rawData_[0][0]; var right = this.rawData_[this.rawData_.length - 1][0]; if (pad) { // Must keep this in sync with dygraph-layout _evaluateLimits()
                        var range = right - left; left -= range * pad; right += range * pad;
                    } return [left, right];
                }; /**
 * Returns the lower- and upper-bound y-axis values for each axis. These are
 * the ranges you'll get if you double-click to zoom out or call resetZoom().
 * The return value is an array of [low, high] tuples, one for each y-axis.
 */Dygraph.prototype.yAxisExtremes = function () { // TODO(danvk): this is pretty inefficient
                    var packed = this.gatherDatasets_(this.rolledSeries_, null); var extremes = packed.extremes; var saveAxes = this.axes_; this.computeYAxisRanges_(extremes); var newAxes = this.axes_; this.axes_ = saveAxes; return newAxes.map(function (axis) { return axis.extremeRange; });
                }; /**
 * Returns the currently-visible y-range for an axis. This can be affected by
 * zooming, panning or a call to updateOptions. Axis indices are zero-based. If
 * called with no arguments, returns the range of the first axis.
 * Returns a two-element array: [bottom, top].
 */Dygraph.prototype.yAxisRange = function (idx) { if (typeof idx == "undefined") idx = 0; if (idx < 0 || idx >= this.axes_.length) { return null; } var axis = this.axes_[idx]; return [axis.computedValueRange[0], axis.computedValueRange[1]]; }; /**
 * Returns the currently-visible y-ranges for each axis. This can be affected by
 * zooming, panning, calls to updateOptions, etc.
 * Returns an array of [bottom, top] pairs, one for each y-axis.
 */Dygraph.prototype.yAxisRanges = function () { var ret = []; for (var i = 0; i < this.axes_.length; i++) { ret.push(this.yAxisRange(i)); } return ret; }; // TODO(danvk): use these functions throughout dygraphs.
/**
 * Convert from data coordinates to canvas/div X/Y coordinates.
 * If specified, do this conversion for the coordinate system of a particular
 * axis. Uses the first axis by default.
 * Returns a two-element array: [X, Y]
 *
 * Note: use toDomXCoord instead of toDomCoords(x, null) and use toDomYCoord
 * instead of toDomCoords(null, y, axis).
 */Dygraph.prototype.toDomCoords = function (x, y, axis) { return [this.toDomXCoord(x), this.toDomYCoord(y, axis)]; }; /**
 * Convert from data x coordinates to canvas/div X coordinate.
 * If specified, do this conversion for the coordinate system of a particular
 * axis.
 * Returns a single value or null if x is null.
 */Dygraph.prototype.toDomXCoord = function (x) { if (x === null) { return null; } var area = this.plotter_.area; var xRange = this.xAxisRange(); return area.x + (x - xRange[0]) / (xRange[1] - xRange[0]) * area.w; }; /**
 * Convert from data x coordinates to canvas/div Y coordinate and optional
 * axis. Uses the first axis by default.
 *
 * returns a single value or null if y is null.
 */Dygraph.prototype.toDomYCoord = function (y, axis) { var pct = this.toPercentYCoord(y, axis); if (pct === null) { return null; } var area = this.plotter_.area; return area.y + pct * area.h; }; /**
 * Convert from canvas/div coords to data coordinates.
 * If specified, do this conversion for the coordinate system of a particular
 * axis. Uses the first axis by default.
 * Returns a two-element array: [X, Y].
 *
 * Note: use toDataXCoord instead of toDataCoords(x, null) and use toDataYCoord
 * instead of toDataCoords(null, y, axis).
 */Dygraph.prototype.toDataCoords = function (x, y, axis) { return [this.toDataXCoord(x), this.toDataYCoord(y, axis)]; }; /**
 * Convert from canvas/div x coordinate to data coordinate.
 *
 * If x is null, this returns null.
 */Dygraph.prototype.toDataXCoord = function (x) { if (x === null) { return null; } var area = this.plotter_.area; var xRange = this.xAxisRange(); if (!this.attributes_.getForAxis("logscale", 'x')) { return xRange[0] + (x - area.x) / area.w * (xRange[1] - xRange[0]); } else { var pct = (x - area.x) / area.w; return utils.logRangeFraction(xRange[0], xRange[1], pct); } }; /**
 * Convert from canvas/div y coord to value.
 *
 * If y is null, this returns null.
 * if axis is null, this uses the first axis.
 */Dygraph.prototype.toDataYCoord = function (y, axis) {
                    if (y === null) { return null; } var area = this.plotter_.area; var yRange = this.yAxisRange(axis); if (typeof axis == "undefined") axis = 0; if (!this.attributes_.getForAxis("logscale", axis)) { return yRange[0] + (area.y + area.h - y) / area.h * (yRange[1] - yRange[0]); } else { // Computing the inverse of toDomCoord.
                        var pct = (y - area.y) / area.h; // Note reversed yRange, y1 is on top with pct==0.
                        return utils.logRangeFraction(yRange[1], yRange[0], pct);
                    }
                }; /**
 * Converts a y for an axis to a percentage from the top to the
 * bottom of the drawing area.
 *
 * If the coordinate represents a value visible on the canvas, then
 * the value will be between 0 and 1, where 0 is the top of the canvas.
 * However, this method will return values outside the range, as
 * values can fall outside the canvas.
 *
 * If y is null, this returns null.
 * if axis is null, this uses the first axis.
 *
 * @param {number} y The data y-coordinate.
 * @param {number} [axis] The axis number on which the data coordinate lives.
 * @return {number} A fraction in [0, 1] where 0 = the top edge.
 */Dygraph.prototype.toPercentYCoord = function (y, axis) {
                    if (y === null) { return null; } if (typeof axis == "undefined") axis = 0; var yRange = this.yAxisRange(axis); var pct; var logscale = this.attributes_.getForAxis("logscale", axis); if (logscale) { var logr0 = utils.log10(yRange[0]); var logr1 = utils.log10(yRange[1]); pct = (logr1 - utils.log10(y)) / (logr1 - logr0); } else { // yRange[1] - y is unit distance from the bottom.
                        // yRange[1] - yRange[0] is the scale of the range.
                        // (yRange[1] - y) / (yRange[1] - yRange[0]) is the % from the bottom.
                        pct = (yRange[1] - y) / (yRange[1] - yRange[0]);
                    } return pct;
                }; /**
 * Converts an x value to a percentage from the left to the right of
 * the drawing area.
 *
 * If the coordinate represents a value visible on the canvas, then
 * the value will be between 0 and 1, where 0 is the left of the canvas.
 * However, this method will return values outside the range, as
 * values can fall outside the canvas.
 *
 * If x is null, this returns null.
 * @param {number} x The data x-coordinate.
 * @return {number} A fraction in [0, 1] where 0 = the left edge.
 */Dygraph.prototype.toPercentXCoord = function (x) {
                    if (x === null) { return null; } var xRange = this.xAxisRange(); var pct; var logscale = this.attributes_.getForAxis("logscale", 'x'); if (logscale === true) { // logscale can be null so we test for true explicitly.
                        var logr0 = utils.log10(xRange[0]); var logr1 = utils.log10(xRange[1]); pct = (utils.log10(x) - logr0) / (logr1 - logr0);
                    } else { // x - xRange[0] is unit distance from the left.
                        // xRange[1] - xRange[0] is the scale of the range.
                        // The full expression below is the % from the left.
                        pct = (x - xRange[0]) / (xRange[1] - xRange[0]);
                    } return pct;
                }; /**
 * Returns the number of columns (including the independent variable).
 * @return {number} The number of columns.
 */Dygraph.prototype.numColumns = function () { if (!this.rawData_) return 0; return this.rawData_[0] ? this.rawData_[0].length : this.attr_("labels").length; }; /**
 * Returns the number of rows (excluding any header/label row).
 * @return {number} The number of rows, less any header.
 */Dygraph.prototype.numRows = function () { if (!this.rawData_) return 0; return this.rawData_.length; }; /**
 * Returns the value in the given row and column. If the row and column exceed
 * the bounds on the data, returns null. Also returns null if the value is
 * missing.
 * @param {number} row The row number of the data (0-based). Row 0 is the
 *     first row of data, not a header row.
 * @param {number} col The column number of the data (0-based)
 * @return {number} The value in the specified cell or null if the row/col
 *     were out of range.
 */Dygraph.prototype.getValue = function (row, col) { if (row < 0 || row > this.rawData_.length) return null; if (col < 0 || col > this.rawData_[row].length) return null; return this.rawData_[row][col]; }; /**
 * Generates interface elements for the Dygraph: a containing div, a div to
 * display the current point, and a textbox to adjust the rolling average
 * period. Also creates the Renderer/Layout elements.
 * @private
 */Dygraph.prototype.createInterface_ = function () { // Create the all-enclosing graph div
                    var enclosing = this.maindiv_; this.graphDiv = document.createElement("div"); // TODO(danvk): any other styles that are useful to set here?
                    this.graphDiv.style.textAlign = 'left'; // This is a CSS "reset"
                    this.graphDiv.style.position = 'relative'; enclosing.appendChild(this.graphDiv); // Create the canvas for interactive parts of the chart.
                    this.canvas_ = utils.createCanvas(); this.canvas_.style.position = "absolute"; // ... and for static parts of the chart.
                    this.hidden_ = this.createPlotKitCanvas_(this.canvas_); this.canvas_ctx_ = utils.getContext(this.canvas_); this.hidden_ctx_ = utils.getContext(this.hidden_); this.resizeElements_(); // The interactive parts of the graph are drawn on top of the chart.
                    this.graphDiv.appendChild(this.hidden_); this.graphDiv.appendChild(this.canvas_); this.mouseEventElement_ = this.createMouseEventElement_(); // Create the grapher
                    this.layout_ = new _dygraphLayout2['default'](this); var dygraph = this; this.mouseMoveHandler_ = function (e) { dygraph.mouseMove_(e); }; this.mouseOutHandler_ = function (e) { // The mouse has left the chart if:
                        // 1. e.target is inside the chart
                        // 2. e.relatedTarget is outside the chart
                        var target = e.target || e.fromElement; var relatedTarget = e.relatedTarget || e.toElement; if (utils.isNodeContainedBy(target, dygraph.graphDiv) && !utils.isNodeContainedBy(relatedTarget, dygraph.graphDiv)) { dygraph.mouseOut_(e); }
                    }; this.addAndTrackEvent(window, 'mouseout', this.mouseOutHandler_); this.addAndTrackEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); // Don't recreate and register the resize handler on subsequent calls.
                    // This happens when the graph is resized.
                    if (!this.resizeHandler_) {
                    this.resizeHandler_ = function (e) { dygraph.resize(); }; // Update when the window is resized.
                        // TODO(danvk): drop frames depending on complexity of the chart.
                        this.addAndTrackEvent(window, 'resize', this.resizeHandler_);
                    }
                }; Dygraph.prototype.resizeElements_ = function () {
                    this.graphDiv.style.width = this.width_ + "px"; this.graphDiv.style.height = this.height_ + "px"; var pixelRatioOption = this.getNumericOption('pixelRatio'); var canvasScale = pixelRatioOption || utils.getContextPixelRatio(this.canvas_ctx_); this.canvas_.width = this.width_ * canvasScale; this.canvas_.height = this.height_ * canvasScale; this.canvas_.style.width = this.width_ + "px"; // for IE
                    this.canvas_.style.height = this.height_ + "px"; // for IE
                    if (canvasScale !== 1) { this.canvas_ctx_.scale(canvasScale, canvasScale); } var hiddenScale = pixelRatioOption || utils.getContextPixelRatio(this.hidden_ctx_); this.hidden_.width = this.width_ * hiddenScale; this.hidden_.height = this.height_ * hiddenScale; this.hidden_.style.width = this.width_ + "px"; // for IE
                    this.hidden_.style.height = this.height_ + "px"; // for IE
                    if (hiddenScale !== 1) { this.hidden_ctx_.scale(hiddenScale, hiddenScale); }
                }; /**
 * Detach DOM elements in the dygraph and null out all data references.
 * Calling this when you're done with a dygraph can dramatically reduce memory
 * usage. See, e.g., the tests/perf.html example.
 */Dygraph.prototype.destroy = function () {
                    this.canvas_ctx_.restore(); this.hidden_ctx_.restore(); // Destroy any plugins, in the reverse order that they were registered.
                    for (var i = this.plugins_.length - 1; i >= 0; i--) { var p = this.plugins_.pop(); if (p.plugin.destroy) p.plugin.destroy(); } var removeRecursive = function removeRecursive(node) { while (node.hasChildNodes()) { removeRecursive(node.firstChild); node.removeChild(node.firstChild); } }; this.removeTrackedEvents_(); // remove mouse event handlers (This may not be necessary anymore)
                    utils.removeEvent(window, 'mouseout', this.mouseOutHandler_); utils.removeEvent(this.mouseEventElement_, 'mousemove', this.mouseMoveHandler_); // remove window handlers
                    utils.removeEvent(window, 'resize', this.resizeHandler_); this.resizeHandler_ = null; removeRecursive(this.maindiv_); var nullOut = function nullOut(obj) { for (var n in obj) { if (typeof obj[n] === 'object') { obj[n] = null; } } }; // These may not all be necessary, but it can't hurt...
                    nullOut(this.layout_); nullOut(this.plotter_); nullOut(this);
                }; /**
 * Creates the canvas on which the chart will be drawn. Only the Renderer ever
 * draws on this particular canvas. All Dygraph work (i.e. drawing hover dots
 * or the zoom rectangles) is done on this.canvas_.
 * @param {Object} canvas The Dygraph canvas over which to overlay the plot
 * @return {Object} The newly-created canvas
 * @private
 */Dygraph.prototype.createPlotKitCanvas_ = function (canvas) {
                    var h = utils.createCanvas(); h.style.position = "absolute"; // TODO(danvk): h should be offset from canvas. canvas needs to include
                    // some extra area to make it easier to zoom in on the far left and far
                    // right. h needs to be precisely the plot area, so that clipping occurs.
                    h.style.top = canvas.style.top; h.style.left = canvas.style.left; h.width = this.width_; h.height = this.height_; h.style.width = this.width_ + "px"; // for IE
                    h.style.height = this.height_ + "px"; // for IE
                    return h;
                }; /**
 * Creates an overlay element used to handle mouse events.
 * @return {Object} The mouse event element.
 * @private
 */Dygraph.prototype.createMouseEventElement_ = function () { return this.canvas_; }; /**
 * Generate a set of distinct colors for the data series. This is done with a
 * color wheel. Saturation/Value are customizable, and the hue is
 * equally-spaced around the color wheel. If a custom set of colors is
 * specified, that is used instead.
 * @private
 */Dygraph.prototype.setColors_ = function () {
                    var labels = this.getLabels(); var num = labels.length - 1; this.colors_ = []; this.colorsMap_ = {}; // These are used for when no custom colors are specified.
                    var sat = this.getNumericOption('colorSaturation') || 1.0; var val = this.getNumericOption('colorValue') || 0.5; var half = Math.ceil(num / 2); var colors = this.getOption('colors'); var visibility = this.visibility(); for (var i = 0; i < num; i++) {
                        if (!visibility[i]) { continue; } var label = labels[i + 1]; var colorStr = this.attributes_.getForSeries('color', label); if (!colorStr) {
                            if (colors) { colorStr = colors[i % colors.length]; } else { // alternate colors for high contrast.
                                var idx = i % 2 ? half + (i + 1) / 2 : Math.ceil((i + 1) / 2); var hue = 1.0 * idx / (1 + num); colorStr = utils.hsvToRGB(hue, sat, val);
                            }
                        } this.colors_.push(colorStr); this.colorsMap_[label] = colorStr;
                    }
                }; /**
 * Return the list of colors. This is either the list of colors passed in the
 * attributes or the autogenerated list of rgb(r,g,b) strings.
 * This does not return colors for invisible series.
 * @return {Array.<string>} The list of colors.
 */Dygraph.prototype.getColors = function () { return this.colors_; }; /**
 * Returns a few attributes of a series, i.e. its color, its visibility, which
 * axis it's assigned to, and its column in the original data.
 * Returns null if the series does not exist.
 * Otherwise, returns an object with column, visibility, color and axis properties.
 * The "axis" property will be set to 1 for y1 and 2 for y2.
 * The "column" property can be fed back into getValue(row, column) to get
 * values for this series.
 */Dygraph.prototype.getPropertiesForSeries = function (series_name) { var idx = -1; var labels = this.getLabels(); for (var i = 1; i < labels.length; i++) { if (labels[i] == series_name) { idx = i; break; } } if (idx == -1) return null; return { name: series_name, column: idx, visible: this.visibility()[idx - 1], color: this.colorsMap_[series_name], axis: 1 + this.attributes_.axisForSeries(series_name) }; }; /**
 * Create the text box to adjust the averaging period
 * @private
 */Dygraph.prototype.createRollInterface_ = function () {
                    var _this = this; // Create a roller if one doesn't exist already.
                    var roller = this.roller_; if (!roller) { this.roller_ = roller = document.createElement("input"); roller.type = "text"; roller.style.display = "none"; roller.className = 'dygraph-roller'; this.graphDiv.appendChild(roller); } var display = this.getBooleanOption('showRoller') ? 'block' : 'none'; var area = this.getArea(); var textAttr = { "top": area.y + area.h - 25 + "px", "left": area.x + 1 + "px", "display": display }; roller.size = "2"; roller.value = this.rollPeriod_; utils.update(roller.style, textAttr); roller.onchange = function () { return _this.adjustRoll(roller.value); };
                }; /**
 * Set up all the mouse handlers needed to capture dragging behavior for zoom
 * events.
 * @private
 */Dygraph.prototype.createDragInterface_ = function () {
                    var context = { // Tracks whether the mouse is down right now
                        isZooming: false, isPanning: false, // is this drag part of a pan?
                        is2DPan: false, // if so, is that pan 1- or 2-dimensional?
                        dragStartX: null, // pixel coordinates
                        dragStartY: null, // pixel coordinates
                        dragEndX: null, // pixel coordinates
                        dragEndY: null, // pixel coordinates
                        dragDirection: null, prevEndX: null, // pixel coordinates
                        prevEndY: null, // pixel coordinates
                        prevDragDirection: null, cancelNextDblclick: false, // see comment in dygraph-interaction-model.js
                        // The value on the left side of the graph when a pan operation starts.
                        initialLeftmostDate: null, // The number of units each pixel spans. (This won't be valid for log
                        // scales)
                        xUnitsPerPixel: null, // TODO(danvk): update this comment
                        // The range in second/value units that the viewport encompasses during a
                        // panning operation.
                        dateRange: null, // Top-left corner of the canvas, in DOM coords
                        // TODO(konigsberg): Rename topLeftCanvasX, topLeftCanvasY.
                        px: 0, py: 0, // Values for use with panEdgeFraction, which limit how far outside the
                        // graph's data boundaries it can be panned.
                        boundedDates: null, // [minDate, maxDate]
                        boundedValues: null, // [[minValue, maxValue] ...]
                        // We cover iframes during mouse interactions. See comments in
                        // dygraph-utils.js for more info on why this is a good idea.
                        tarp: new _iframeTarp2['default'](), // contextB is the same thing as this context object but renamed.
                        initializeMouseDown: function initializeMouseDown(event, g, contextB) { // prevents mouse drags from selecting page text.
                            if (event.preventDefault) {
                                event.preventDefault(); // Firefox, Chrome, etc.
                            } else {
                            event.returnValue = false; // IE
                                event.cancelBubble = true;
                            } var canvasPos = utils.findPos(g.canvas_); contextB.px = canvasPos.x; contextB.py = canvasPos.y; contextB.dragStartX = utils.dragGetX_(event, contextB); contextB.dragStartY = utils.dragGetY_(event, contextB); contextB.cancelNextDblclick = false; contextB.tarp.cover();
                        }, destroy: function destroy() { var context = this; if (context.isZooming || context.isPanning) { context.isZooming = false; context.dragStartX = null; context.dragStartY = null; } if (context.isPanning) { context.isPanning = false; context.draggingDate = null; context.dateRange = null; for (var i = 0; i < self.axes_.length; i++) { delete self.axes_[i].draggingValue; delete self.axes_[i].dragValueRange; } } context.tarp.uncover(); }
                    }; var interactionModel = this.getOption("interactionModel"); // Self is the graph.
                    var self = this; // Function that binds the graph and context to the handler.
                    var bindHandler = function bindHandler(handler) { return function (event) { handler(event, self, context); }; }; for (var eventName in interactionModel) { if (!interactionModel.hasOwnProperty(eventName)) continue; this.addAndTrackEvent(this.mouseEventElement_, eventName, bindHandler(interactionModel[eventName])); } // If the user releases the mouse button during a drag, but not over the
                    // canvas, then it doesn't count as a zooming action.
                    if (!interactionModel.willDestroyContextMyself) { var mouseUpHandler = function mouseUpHandler(event) { context.destroy(); }; this.addAndTrackEvent(document, 'mouseup', mouseUpHandler); }
                }; /**
 * Draw a gray zoom rectangle over the desired area of the canvas. Also clears
 * up any previous zoom rectangles that were drawn. This could be optimized to
 * avoid extra redrawing, but it's tricky to avoid interactions with the status
 * dots.
 *
 * @param {number} direction the direction of the zoom rectangle. Acceptable
 *     values are utils.HORIZONTAL and utils.VERTICAL.
 * @param {number} startX The X position where the drag started, in canvas
 *     coordinates.
 * @param {number} endX The current X position of the drag, in canvas coords.
 * @param {number} startY The Y position where the drag started, in canvas
 *     coordinates.
 * @param {number} endY The current Y position of the drag, in canvas coords.
 * @param {number} prevDirection the value of direction on the previous call to
 *     this function. Used to avoid excess redrawing
 * @param {number} prevEndX The value of endX on the previous call to this
 *     function. Used to avoid excess redrawing
 * @param {number} prevEndY The value of endY on the previous call to this
 *     function. Used to avoid excess redrawing
 * @private
 */Dygraph.prototype.drawZoomRect_ = function (direction, startX, endX, startY, endY, prevDirection, prevEndX, prevEndY) {
                    var ctx = this.canvas_ctx_; // Clean up from the previous rect if necessary
                    if (prevDirection == utils.HORIZONTAL) { ctx.clearRect(Math.min(startX, prevEndX), this.layout_.getPlotArea().y, Math.abs(startX - prevEndX), this.layout_.getPlotArea().h); } else if (prevDirection == utils.VERTICAL) { ctx.clearRect(this.layout_.getPlotArea().x, Math.min(startY, prevEndY), this.layout_.getPlotArea().w, Math.abs(startY - prevEndY)); } // Draw a light-grey rectangle to show the new viewing area
                    if (direction == utils.HORIZONTAL) { if (endX && startX) { ctx.fillStyle = "rgba(128,128,128,0.33)"; ctx.fillRect(Math.min(startX, endX), this.layout_.getPlotArea().y, Math.abs(endX - startX), this.layout_.getPlotArea().h); } } else if (direction == utils.VERTICAL) { if (endY && startY) { ctx.fillStyle = "rgba(128,128,128,0.33)"; ctx.fillRect(this.layout_.getPlotArea().x, Math.min(startY, endY), this.layout_.getPlotArea().w, Math.abs(endY - startY)); } }
                }; /**
 * Clear the zoom rectangle (and perform no zoom).
 * @private
 */Dygraph.prototype.clearZoomRect_ = function () { this.currentZoomRectArgs_ = null; this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_); }; /**
 * Zoom to something containing [lowX, highX]. These are pixel coordinates in
 * the canvas. The exact zoom window may be slightly larger if there are no data
 * points near lowX or highX. Don't confuse this function with doZoomXDates,
 * which accepts dates that match the raw data. This function redraws the graph.
 *
 * @param {number} lowX The leftmost pixel value that should be visible.
 * @param {number} highX The rightmost pixel value that should be visible.
 * @private
 */Dygraph.prototype.doZoomX_ = function (lowX, highX) {
                this.currentZoomRectArgs_ = null; // Find the earliest and latest dates contained in this canvasx range.
                    // Convert the call to date ranges of the raw data.
                    var minDate = this.toDataXCoord(lowX); var maxDate = this.toDataXCoord(highX); this.doZoomXDates_(minDate, maxDate);
                }; /**
 * Zoom to something containing [minDate, maxDate] values. Don't confuse this
 * method with doZoomX which accepts pixel coordinates. This function redraws
 * the graph.
 *
 * @param {number} minDate The minimum date that should be visible.
 * @param {number} maxDate The maximum date that should be visible.
 * @private
 */Dygraph.prototype.doZoomXDates_ = function (minDate, maxDate) {
                    var _this2 = this; // TODO(danvk): when xAxisRange is null (i.e. "fit to data", the animation
                    // can produce strange effects. Rather than the x-axis transitioning slowly
                    // between values, it can jerk around.)
                    var old_window = this.xAxisRange(); var new_window = [minDate, maxDate]; var zoomCallback = this.getFunctionOption('zoomCallback'); this.doAnimatedZoom(old_window, new_window, null, null, function () { if (zoomCallback) { zoomCallback.call(_this2, minDate, maxDate, _this2.yAxisRanges()); } });
                }; /**
 * Zoom to something containing [lowY, highY]. These are pixel coordinates in
 * the canvas. This function redraws the graph.
 *
 * @param {number} lowY The topmost pixel value that should be visible.
 * @param {number} highY The lowest pixel value that should be visible.
 * @private
 */Dygraph.prototype.doZoomY_ = function (lowY, highY) {
                    var _this3 = this; this.currentZoomRectArgs_ = null; // Find the highest and lowest values in pixel range for each axis.
                    // Note that lowY (in pixels) corresponds to the max Value (in data coords).
                    // This is because pixels increase as you go down on the screen, whereas data
                    // coordinates increase as you go up the screen.
                    var oldValueRanges = this.yAxisRanges(); var newValueRanges = []; for (var i = 0; i < this.axes_.length; i++) { var hi = this.toDataYCoord(lowY, i); var low = this.toDataYCoord(highY, i); newValueRanges.push([low, hi]); } var zoomCallback = this.getFunctionOption('zoomCallback'); this.doAnimatedZoom(null, null, oldValueRanges, newValueRanges, function () { if (zoomCallback) { var _xAxisRange = _this3.xAxisRange(); var _xAxisRange2 = _slicedToArray(_xAxisRange, 2); var minX = _xAxisRange2[0]; var maxX = _xAxisRange2[1]; zoomCallback.call(_this3, minX, maxX, _this3.yAxisRanges()); } });
                }; /**
 * Transition function to use in animations. Returns values between 0.0
 * (totally old values) and 1.0 (totally new values) for each frame.
 * @private
 */Dygraph.zoomAnimationFunction = function (frame, numFrames) { var k = 1.5; return (1.0 - Math.pow(k, -frame)) / (1.0 - Math.pow(k, -numFrames)); }; /**
 * Reset the zoom to the original view coordinates. This is the same as
 * double-clicking on the graph.
 */Dygraph.prototype.resetZoom = function () {
                    var _this4 = this; var dirtyX = this.isZoomed('x'); var dirtyY = this.isZoomed('y'); var dirty = dirtyX || dirtyY; // Clear any selection, since it's likely to be drawn in the wrong place.
                    this.clearSelection(); if (!dirty) return; // Calculate extremes to avoid lack of padding on reset.
                    var _xAxisExtremes = this.xAxisExtremes(); var _xAxisExtremes2 = _slicedToArray(_xAxisExtremes, 2); var minDate = _xAxisExtremes2[0]; var maxDate = _xAxisExtremes2[1]; var animatedZooms = this.getBooleanOption('animatedZooms'); var zoomCallback = this.getFunctionOption('zoomCallback'); // TODO(danvk): merge this block w/ the code below.
                    // TODO(danvk): factor out a generic, public zoomTo method.
                    if (!animatedZooms) { this.dateWindow_ = null; this.axes_.forEach(function (axis) { if (axis.valueRange) delete axis.valueRange; }); this.drawGraph_(); if (zoomCallback) { zoomCallback.call(this, minDate, maxDate, this.yAxisRanges()); } return; } var oldWindow = null, newWindow = null, oldValueRanges = null, newValueRanges = null; if (dirtyX) { oldWindow = this.xAxisRange(); newWindow = [minDate, maxDate]; } if (dirtyY) { oldValueRanges = this.yAxisRanges(); newValueRanges = this.yAxisExtremes(); } this.doAnimatedZoom(oldWindow, newWindow, oldValueRanges, newValueRanges, function () { _this4.dateWindow_ = null; _this4.axes_.forEach(function (axis) { if (axis.valueRange) delete axis.valueRange; }); if (zoomCallback) { zoomCallback.call(_this4, minDate, maxDate, _this4.yAxisRanges()); } });
                }; /**
 * Combined animation logic for all zoom functions.
 * either the x parameters or y parameters may be null.
 * @private
 */Dygraph.prototype.doAnimatedZoom = function (oldXRange, newXRange, oldYRanges, newYRanges, callback) { var _this5 = this; var steps = this.getBooleanOption("animatedZooms") ? Dygraph.ANIMATION_STEPS : 1; var windows = []; var valueRanges = []; var step, frac; if (oldXRange !== null && newXRange !== null) { for (step = 1; step <= steps; step++) { frac = Dygraph.zoomAnimationFunction(step, steps); windows[step - 1] = [oldXRange[0] * (1 - frac) + frac * newXRange[0], oldXRange[1] * (1 - frac) + frac * newXRange[1]]; } } if (oldYRanges !== null && newYRanges !== null) { for (step = 1; step <= steps; step++) { frac = Dygraph.zoomAnimationFunction(step, steps); var thisRange = []; for (var j = 0; j < this.axes_.length; j++) { thisRange.push([oldYRanges[j][0] * (1 - frac) + frac * newYRanges[j][0], oldYRanges[j][1] * (1 - frac) + frac * newYRanges[j][1]]); } valueRanges[step - 1] = thisRange; } } utils.repeatAndCleanup(function (step) { if (valueRanges.length) { for (var i = 0; i < _this5.axes_.length; i++) { var w = valueRanges[step][i]; _this5.axes_[i].valueRange = [w[0], w[1]]; } } if (windows.length) { _this5.dateWindow_ = windows[step]; } _this5.drawGraph_(); }, steps, Dygraph.ANIMATION_DURATION / steps, callback); }; /**
 * Get the current graph's area object.
 *
 * Returns: {x, y, w, h}
 */Dygraph.prototype.getArea = function () { return this.plotter_.area; }; /**
 * Convert a mouse event to DOM coordinates relative to the graph origin.
 *
 * Returns a two-element array: [X, Y].
 */Dygraph.prototype.eventToDomCoords = function (event) { if (event.offsetX && event.offsetY) { return [event.offsetX, event.offsetY]; } else { var eventElementPos = utils.findPos(this.mouseEventElement_); var canvasx = utils.pageX(event) - eventElementPos.x; var canvasy = utils.pageY(event) - eventElementPos.y; return [canvasx, canvasy]; } }; /**
 * Given a canvas X coordinate, find the closest row.
 * @param {number} domX graph-relative DOM X coordinate
 * Returns {number} row number.
 * @private
 */Dygraph.prototype.findClosestRow = function (domX) { var minDistX = Infinity; var closestRow = -1; var sets = this.layout_.points; for (var i = 0; i < sets.length; i++) { var points = sets[i]; var len = points.length; for (var j = 0; j < len; j++) { var point = points[j]; if (!utils.isValidPoint(point, true)) continue; var dist = Math.abs(point.canvasx - domX); if (dist < minDistX) { minDistX = dist; closestRow = point.idx; } } } return closestRow; }; /**
 * Given canvas X,Y coordinates, find the closest point.
 *
 * This finds the individual data point across all visible series
 * that's closest to the supplied DOM coordinates using the standard
 * Euclidean X,Y distance.
 *
 * @param {number} domX graph-relative DOM X coordinate
 * @param {number} domY graph-relative DOM Y coordinate
 * Returns: {row, seriesName, point}
 * @private
 */Dygraph.prototype.findClosestPoint = function (domX, domY) { var minDist = Infinity; var dist, dx, dy, point, closestPoint, closestSeries, closestRow; for (var setIdx = this.layout_.points.length - 1; setIdx >= 0; --setIdx) { var points = this.layout_.points[setIdx]; for (var i = 0; i < points.length; ++i) { point = points[i]; if (!utils.isValidPoint(point)) continue; dx = point.canvasx - domX; dy = point.canvasy - domY; dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; closestPoint = point; closestSeries = setIdx; closestRow = point.idx; } } } var name = this.layout_.setNames[closestSeries]; return { row: closestRow, seriesName: name, point: closestPoint }; }; /**
 * Given canvas X,Y coordinates, find the touched area in a stacked graph.
 *
 * This first finds the X data point closest to the supplied DOM X coordinate,
 * then finds the series which puts the Y coordinate on top of its filled area,
 * using linear interpolation between adjacent point pairs.
 *
 * @param {number} domX graph-relative DOM X coordinate
 * @param {number} domY graph-relative DOM Y coordinate
 * Returns: {row, seriesName, point}
 * @private
 */Dygraph.prototype.findStackedPoint = function (domX, domY) {
                    var row = this.findClosestRow(domX); var closestPoint, closestSeries; for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {
                        var boundary = this.getLeftBoundary_(setIdx); var rowIdx = row - boundary; var points = this.layout_.points[setIdx]; if (rowIdx >= points.length) continue; var p1 = points[rowIdx]; if (!utils.isValidPoint(p1)) continue; var py = p1.canvasy; if (domX > p1.canvasx && rowIdx + 1 < points.length) { // interpolate series Y value using next point
                            var p2 = points[rowIdx + 1]; if (utils.isValidPoint(p2)) { var dx = p2.canvasx - p1.canvasx; if (dx > 0) { var r = (domX - p1.canvasx) / dx; py += r * (p2.canvasy - p1.canvasy); } }
                        } else if (domX < p1.canvasx && rowIdx > 0) { // interpolate series Y value using previous point
                            var p0 = points[rowIdx - 1]; if (utils.isValidPoint(p0)) { var dx = p1.canvasx - p0.canvasx; if (dx > 0) { var r = (p1.canvasx - domX) / dx; py += r * (p0.canvasy - p1.canvasy); } }
                        } // Stop if the point (domX, py) is above this series' upper edge
                        if (setIdx === 0 || py < domY) { closestPoint = p1; closestSeries = setIdx; }
                    } var name = this.layout_.setNames[closestSeries]; return { row: row, seriesName: name, point: closestPoint };
                }; /**
 * When the mouse moves in the canvas, display information about a nearby data
 * point and draw dots over those points in the data series. This function
 * takes care of cleanup of previously-drawn dots.
 * @param {Object} event The mousemove event from the browser.
 * @private
 */Dygraph.prototype.mouseMove_ = function (event) { // This prevents JS errors when mousing over the canvas before data loads.
                    var points = this.layout_.points; if (points === undefined || points === null) return; var canvasCoords = this.eventToDomCoords(event); var canvasx = canvasCoords[0]; var canvasy = canvasCoords[1]; var highlightSeriesOpts = this.getOption("highlightSeriesOpts"); var selectionChanged = false; if (highlightSeriesOpts && !this.isSeriesLocked()) { var closest; if (this.getBooleanOption("stackedGraph")) { closest = this.findStackedPoint(canvasx, canvasy); } else { closest = this.findClosestPoint(canvasx, canvasy); } selectionChanged = this.setSelection(closest.row, closest.seriesName); } else { var idx = this.findClosestRow(canvasx); selectionChanged = this.setSelection(idx); } var callback = this.getFunctionOption("highlightCallback"); if (callback && selectionChanged) { callback.call(this, event, this.lastx_, this.selPoints_, this.lastRow_, this.highlightSet_); }
                }; /**
 * Fetch left offset from the specified set index or if not passed, the
 * first defined boundaryIds record (see bug #236).
 * @private
 */Dygraph.prototype.getLeftBoundary_ = function (setIdx) { if (this.boundaryIds_[setIdx]) { return this.boundaryIds_[setIdx][0]; } else { for (var i = 0; i < this.boundaryIds_.length; i++) { if (this.boundaryIds_[i] !== undefined) { return this.boundaryIds_[i][0]; } } return 0; } }; Dygraph.prototype.animateSelection_ = function (direction) {
                    var totalSteps = 10; var millis = 30; if (this.fadeLevel === undefined) this.fadeLevel = 0; if (this.animateId === undefined) this.animateId = 0; var start = this.fadeLevel; var steps = direction < 0 ? start : totalSteps - start; if (steps <= 0) { if (this.fadeLevel) { this.updateSelection_(1.0); } return; } var thisId = ++this.animateId; var that = this; var cleanupIfClearing = function cleanupIfClearing() { // if we haven't reached fadeLevel 0 in the max frame time,
                        // ensure that the clear happens and just go to 0
                        if (that.fadeLevel !== 0 && direction < 0) { that.fadeLevel = 0; that.clearSelection(); }
                    }; utils.repeatAndCleanup(function (n) { // ignore simultaneous animations
                        if (that.animateId != thisId) return; that.fadeLevel += direction; if (that.fadeLevel === 0) { that.clearSelection(); } else { that.updateSelection_(that.fadeLevel / totalSteps); }
                    }, steps, millis, cleanupIfClearing);
                }; /**
 * Draw dots over the selectied points in the data series. This function
 * takes care of cleanup of previously-drawn dots.
 * @private
 */Dygraph.prototype.updateSelection_ = function (opt_animFraction) { /*var defaultPrevented = */this.cascadeEvents_('select', { selectedRow: this.lastRow_ === -1 ? undefined : this.lastRow_, selectedX: this.lastx_ === -1 ? undefined : this.lastx_, selectedPoints: this.selPoints_ }); // TODO(danvk): use defaultPrevented here?
                    // Clear the previously drawn vertical, if there is one
                    var i; var ctx = this.canvas_ctx_; if (this.getOption('highlightSeriesOpts')) {
                        ctx.clearRect(0, 0, this.width_, this.height_); var alpha = 1.0 - this.getNumericOption('highlightSeriesBackgroundAlpha'); var backgroundColor = utils.toRGB_(this.getOption('highlightSeriesBackgroundColor')); if (alpha) { // Activating background fade includes an animation effect for a gradual
                            // fade. TODO(klausw): make this independently configurable if it causes
                            // issues? Use a shared preference to control animations?
                            var animateBackgroundFade = true; if (animateBackgroundFade) {
                                if (opt_animFraction === undefined) { // start a new animation
                                    this.animateSelection_(1); return;
                                } alpha *= opt_animFraction;
                            } ctx.fillStyle = 'rgba(' + backgroundColor.r + ',' + backgroundColor.g + ',' + backgroundColor.b + ',' + alpha + ')'; ctx.fillRect(0, 0, this.width_, this.height_);
                        } // Redraw only the highlighted series in the interactive canvas (not the
                        // static plot canvas, which is where series are usually drawn).
                        this.plotter_._renderLineChart(this.highlightSet_, ctx);
                    } else if (this.previousVerticalX_ >= 0) { // Determine the maximum highlight circle size.
                        var maxCircleSize = 0; var labels = this.attr_('labels'); for (i = 1; i < labels.length; i++) { var r = this.getNumericOption('highlightCircleSize', labels[i]); if (r > maxCircleSize) maxCircleSize = r; } var px = this.previousVerticalX_; ctx.clearRect(px - maxCircleSize - 1, 0, 2 * maxCircleSize + 2, this.height_);
                    } if (this.selPoints_.length > 0) { // Draw colored circles over the center of each selected point
                        var canvasx = this.selPoints_[0].canvasx; ctx.save(); for (i = 0; i < this.selPoints_.length; i++) { var pt = this.selPoints_[i]; if (isNaN(pt.canvasy)) continue; var circleSize = this.getNumericOption('highlightCircleSize', pt.name); var callback = this.getFunctionOption("drawHighlightPointCallback", pt.name); var color = this.plotter_.colors[pt.name]; if (!callback) { callback = utils.Circles.DEFAULT; } ctx.lineWidth = this.getNumericOption('strokeWidth', pt.name); ctx.strokeStyle = color; ctx.fillStyle = color; callback.call(this, this, pt.name, ctx, canvasx, pt.canvasy, color, circleSize, pt.idx); } ctx.restore(); this.previousVerticalX_ = canvasx;
                    }
                }; /**
 * Manually set the selected points and display information about them in the
 * legend. The selection can be cleared using clearSelection() and queried
 * using getSelection().
 *
 * To set a selected series but not a selected point, call setSelection with
 * row=false and the selected series name.
 *
 * @param {number} row Row number that should be highlighted (i.e. appear with
 * hover dots on the chart).
 * @param {seriesName} optional series name to highlight that series with the
 * the highlightSeriesOpts setting.
 * @param { locked } optional If true, keep seriesName selected when mousing
 * over the graph, disabling closest-series highlighting. Call clearSelection()
 * to unlock it.
 */Dygraph.prototype.setSelection = function (row, opt_seriesName, opt_locked) { // Extract the points we've selected
                    this.selPoints_ = []; var changed = false; if (row !== false && row >= 0) {
                        if (row != this.lastRow_) changed = true; this.lastRow_ = row; for (var setIdx = 0; setIdx < this.layout_.points.length; ++setIdx) {
                            var points = this.layout_.points[setIdx]; // Check if the point at the appropriate index is the point we're looking
                            // for.  If it is, just use it, otherwise search the array for a point
                            // in the proper place.
                            var setRow = row - this.getLeftBoundary_(setIdx); if (setRow >= 0 && setRow < points.length && points[setRow].idx == row) { var point = points[setRow]; if (point.yval !== null) this.selPoints_.push(point); } else { for (var pointIdx = 0; pointIdx < points.length; ++pointIdx) { var point = points[pointIdx]; if (point.idx == row) { if (point.yval !== null) { this.selPoints_.push(point); } break; } } }
                        }
                    } else { if (this.lastRow_ >= 0) changed = true; this.lastRow_ = -1; } if (this.selPoints_.length) { this.lastx_ = this.selPoints_[0].xval; } else { this.lastx_ = -1; } if (opt_seriesName !== undefined) { if (this.highlightSet_ !== opt_seriesName) changed = true; this.highlightSet_ = opt_seriesName; } if (opt_locked !== undefined) { this.lockedSet_ = opt_locked; } if (changed) { this.updateSelection_(undefined); } return changed;
                }; /**
 * The mouse has left the canvas. Clear out whatever artifacts remain
 * @param {Object} event the mouseout event from the browser.
 * @private
 */Dygraph.prototype.mouseOut_ = function (event) { if (this.getFunctionOption("unhighlightCallback")) { this.getFunctionOption("unhighlightCallback").call(this, event); } if (this.getBooleanOption("hideOverlayOnMouseOut") && !this.lockedSet_) { this.clearSelection(); } }; /**
 * Clears the current selection (i.e. points that were highlighted by moving
 * the mouse over the chart).
 */Dygraph.prototype.clearSelection = function () {
                    this.cascadeEvents_('deselect', {}); this.lockedSet_ = false; // Get rid of the overlay data
                    if (this.fadeLevel) { this.animateSelection_(-1); return; } this.canvas_ctx_.clearRect(0, 0, this.width_, this.height_); this.fadeLevel = 0; this.selPoints_ = []; this.lastx_ = -1; this.lastRow_ = -1; this.highlightSet_ = null;
                }; /**
 * Returns the number of the currently selected row. To get data for this row,
 * you can use the getValue method.
 * @return {number} row number, or -1 if nothing is selected
 */Dygraph.prototype.getSelection = function () { if (!this.selPoints_ || this.selPoints_.length < 1) { return -1; } for (var setIdx = 0; setIdx < this.layout_.points.length; setIdx++) { var points = this.layout_.points[setIdx]; for (var row = 0; row < points.length; row++) { if (points[row].x == this.selPoints_[0].x) { return points[row].idx; } } } return -1; }; /**
 * Returns the name of the currently-highlighted series.
 * Only available when the highlightSeriesOpts option is in use.
 */Dygraph.prototype.getHighlightSeries = function () { return this.highlightSet_; }; /**
 * Returns true if the currently-highlighted series was locked
 * via setSelection(..., seriesName, true).
 */Dygraph.prototype.isSeriesLocked = function () { return this.lockedSet_; }; /**
 * Fires when there's data available to be graphed.
 * @param {string} data Raw CSV data to be plotted
 * @private
 */Dygraph.prototype.loadedEvent_ = function (data) { this.rawData_ = this.parseCSV_(data); this.cascadeDataDidUpdateEvent_(); this.predraw_(); }; /**
 * Add ticks on the x-axis representing years, months, quarters, weeks, or days
 * @private
 */Dygraph.prototype.addXTicks_ = function () { // Determine the correct ticks scale on the x-axis: quarterly, monthly, ...
                    var range; if (this.dateWindow_) { range = [this.dateWindow_[0], this.dateWindow_[1]]; } else { range = this.xAxisExtremes(); } var xAxisOptionsView = this.optionsViewForAxis_('x'); var xTicks = xAxisOptionsView('ticker')(range[0], range[1], this.plotter_.area.w, // TODO(danvk): should be area.width
                        xAxisOptionsView, this); // var msg = 'ticker(' + range[0] + ', ' + range[1] + ', ' + this.width_ + ', ' + this.attr_('pixelsPerXLabel') + ') -> ' + JSON.stringify(xTicks);
                    // console.log(msg);
                    this.layout_.setXTicks(xTicks);
                }; /**
 * Returns the correct handler class for the currently set options.
 * @private
 */Dygraph.prototype.getHandlerClass_ = function () { var handlerClass; if (this.attr_('dataHandler')) { handlerClass = this.attr_('dataHandler'); } else if (this.fractions_) { if (this.getBooleanOption('errorBars')) { handlerClass = _datahandlerBarsFractions2['default']; } else { handlerClass = _datahandlerDefaultFractions2['default']; } } else if (this.getBooleanOption('customBars')) { handlerClass = _datahandlerBarsCustom2['default']; } else if (this.getBooleanOption('errorBars')) { handlerClass = _datahandlerBarsError2['default']; } else { handlerClass = _datahandlerDefault2['default']; } return handlerClass; }; /**
 * @private
 * This function is called once when the chart's data is changed or the options
 * dictionary is updated. It is _not_ called when the user pans or zooms. The
 * idea is that values derived from the chart's data can be computed here,
 * rather than every time the chart is drawn. This includes things like the
 * number of axes, rolling averages, etc.
 */Dygraph.prototype.predraw_ = function () {
                    var start = new Date(); // Create the correct dataHandler
                    this.dataHandler_ = new (this.getHandlerClass_())(); this.layout_.computePlotArea(); // TODO(danvk): move more computations out of drawGraph_ and into here.
                    this.computeYAxes_(); if (!this.is_initial_draw_) { this.canvas_ctx_.restore(); this.hidden_ctx_.restore(); } this.canvas_ctx_.save(); this.hidden_ctx_.save(); // Create a new plotter.
                    this.plotter_ = new _dygraphCanvas2['default'](this, this.hidden_, this.hidden_ctx_, this.layout_); // The roller sits in the bottom left corner of the chart. We don't know where
                    // this will be until the options are available, so it's positioned here.
                    this.createRollInterface_(); this.cascadeEvents_('predraw'); // Convert the raw data (a 2D array) into the internal format and compute
                    // rolling averages.
                    this.rolledSeries_ = [null]; // x-axis is the first series and it's special
                    for (var i = 1; i < this.numColumns(); i++) { // var logScale = this.attr_('logscale', i); // TODO(klausw): this looks wrong // konigsberg thinks so too.
                        var series = this.dataHandler_.extractSeries(this.rawData_, i, this.attributes_); if (this.rollPeriod_ > 1) { series = this.dataHandler_.rollingAverage(series, this.rollPeriod_, this.attributes_); } this.rolledSeries_.push(series);
                    } // If the data or options have changed, then we'd better redraw.
                    this.drawGraph_(); // This is used to determine whether to do various animations.
                    var end = new Date(); this.drawingTimeMs_ = end - start;
                }; /**
 * Point structure.
 *
 * xval_* and yval_* are the original unscaled data values,
 * while x_* and y_* are scaled to the range (0.0-1.0) for plotting.
 * yval_stacked is the cumulative Y value used for stacking graphs,
 * and bottom/top/minus/plus are used for error bar graphs.
 *
 * @typedef {{
 *     idx: number,
 *     name: string,
 *     x: ?number,
 *     xval: ?number,
 *     y_bottom: ?number,
 *     y: ?number,
 *     y_stacked: ?number,
 *     y_top: ?number,
 *     yval_minus: ?number,
 *     yval: ?number,
 *     yval_plus: ?number,
 *     yval_stacked
 * }}
 */Dygraph.PointType = undefined; /**
 * Calculates point stacking for stackedGraph=true.
 *
 * For stacking purposes, interpolate or extend neighboring data across
 * NaN values based on stackedGraphNaNFill settings. This is for display
 * only, the underlying data value as shown in the legend remains NaN.
 *
 * @param {Array.<Dygraph.PointType>} points Point array for a single series.
 *     Updates each Point's yval_stacked property.
 * @param {Array.<number>} cumulativeYval Accumulated top-of-graph stacked Y
 *     values for the series seen so far. Index is the row number. Updated
 *     based on the current series's values.
 * @param {Array.<number>} seriesExtremes Min and max values, updated
 *     to reflect the stacked values.
 * @param {string} fillMethod Interpolation method, one of 'all', 'inside', or
 *     'none'.
 * @private
 */Dygraph.stackPoints_ = function (points, cumulativeYval, seriesExtremes, fillMethod) {
                    var lastXval = null; var prevPoint = null; var nextPoint = null; var nextPointIdx = -1; // Find the next stackable point starting from the given index.
                    var updateNextPoint = function updateNextPoint(idx) { // If we've previously found a non-NaN point and haven't gone past it yet,
                        // just use that.
                        if (nextPointIdx >= idx) return; // We haven't found a non-NaN point yet or have moved past it,
                        // look towards the right to find a non-NaN point.
                        for (var j = idx; j < points.length; ++j) { // Clear out a previously-found point (if any) since it's no longer
                            // valid, we shouldn't use it for interpolation anymore.
                            nextPoint = null; if (!isNaN(points[j].yval) && points[j].yval !== null) { nextPointIdx = j; nextPoint = points[j]; break; }
                        }
                    }; for (var i = 0; i < points.length; ++i) {
                        var point = points[i]; var xval = point.xval; if (cumulativeYval[xval] === undefined) { cumulativeYval[xval] = 0; } var actualYval = point.yval; if (isNaN(actualYval) || actualYval === null) {
                            if (fillMethod == 'none') { actualYval = 0; } else { // Interpolate/extend for stacking purposes if possible.
                                updateNextPoint(i); if (prevPoint && nextPoint && fillMethod != 'none') { // Use linear interpolation between prevPoint and nextPoint.
                                    actualYval = prevPoint.yval + (nextPoint.yval - prevPoint.yval) * ((xval - prevPoint.xval) / (nextPoint.xval - prevPoint.xval));
                                } else if (prevPoint && fillMethod == 'all') { actualYval = prevPoint.yval; } else if (nextPoint && fillMethod == 'all') { actualYval = nextPoint.yval; } else { actualYval = 0; }
                            }
                        } else { prevPoint = point; } var stackedYval = cumulativeYval[xval]; if (lastXval != xval) { // If an x-value is repeated, we ignore the duplicates.
                            stackedYval += actualYval; cumulativeYval[xval] = stackedYval;
                        } lastXval = xval; point.yval_stacked = stackedYval; if (stackedYval > seriesExtremes[1]) { seriesExtremes[1] = stackedYval; } if (stackedYval < seriesExtremes[0]) { seriesExtremes[0] = stackedYval; }
                    }
                }; /**
 * Loop over all fields and create datasets, calculating extreme y-values for
 * each series and extreme x-indices as we go.
 *
 * dateWindow is passed in as an explicit parameter so that we can compute
 * extreme values "speculatively", i.e. without actually setting state on the
 * dygraph.
 *
 * @param {Array.<Array.<Array.<(number|Array<number>)>>} rolledSeries, where
 *     rolledSeries[seriesIndex][row] = raw point, where
 *     seriesIndex is the column number starting with 1, and
 *     rawPoint is [x,y] or [x, [y, err]] or [x, [y, yminus, yplus]].
 * @param {?Array.<number>} dateWindow [xmin, xmax] pair, or null.
 * @return {{
 *     points: Array.<Array.<Dygraph.PointType>>,
 *     seriesExtremes: Array.<Array.<number>>,
 *     boundaryIds: Array.<number>}}
 * @private
 */Dygraph.prototype.gatherDatasets_ = function (rolledSeries, dateWindow) {
                    var boundaryIds = []; var points = []; var cumulativeYval = []; // For stacked series.
                    var extremes = {}; // series name -> [low, high]
                    var seriesIdx, sampleIdx; var firstIdx, lastIdx; var axisIdx; // Loop over the fields (series).  Go from the last to the first,
                    // because if they're stacked that's how we accumulate the values.
                    var num_series = rolledSeries.length - 1; var series; for (seriesIdx = num_series; seriesIdx >= 1; seriesIdx--) {
                        if (!this.visibility()[seriesIdx - 1]) continue; // Prune down to the desired range, if necessary (for zooming)
                        // Because there can be lines going to points outside of the visible area,
                        // we actually prune to visible points, plus one on either side.
                        if (dateWindow) {
                            series = rolledSeries[seriesIdx]; var low = dateWindow[0]; var high = dateWindow[1]; // TODO(danvk): do binary search instead of linear search.
                            // TODO(danvk): pass firstIdx and lastIdx directly to the renderer.
                            firstIdx = null; lastIdx = null; for (sampleIdx = 0; sampleIdx < series.length; sampleIdx++) { if (series[sampleIdx][0] >= low && firstIdx === null) { firstIdx = sampleIdx; } if (series[sampleIdx][0] <= high) { lastIdx = sampleIdx; } } if (firstIdx === null) firstIdx = 0; var correctedFirstIdx = firstIdx; var isInvalidValue = true; while (isInvalidValue && correctedFirstIdx > 0) {
                                correctedFirstIdx--; // check if the y value is null.
                                isInvalidValue = series[correctedFirstIdx][1] === null;
                            } if (lastIdx === null) lastIdx = series.length - 1; var correctedLastIdx = lastIdx; isInvalidValue = true; while (isInvalidValue && correctedLastIdx < series.length - 1) { correctedLastIdx++; isInvalidValue = series[correctedLastIdx][1] === null; } if (correctedFirstIdx !== firstIdx) { firstIdx = correctedFirstIdx; } if (correctedLastIdx !== lastIdx) { lastIdx = correctedLastIdx; } boundaryIds[seriesIdx - 1] = [firstIdx, lastIdx]; // .slice's end is exclusive, we want to include lastIdx.
                            series = series.slice(firstIdx, lastIdx + 1);
                        } else { series = rolledSeries[seriesIdx]; boundaryIds[seriesIdx - 1] = [0, series.length - 1]; } var seriesName = this.attr_("labels")[seriesIdx]; var seriesExtremes = this.dataHandler_.getExtremeYValues(series, dateWindow, this.getBooleanOption("stepPlot", seriesName)); var seriesPoints = this.dataHandler_.seriesToPoints(series, seriesName, boundaryIds[seriesIdx - 1][0]); if (this.getBooleanOption("stackedGraph")) { axisIdx = this.attributes_.axisForSeries(seriesName); if (cumulativeYval[axisIdx] === undefined) { cumulativeYval[axisIdx] = []; } Dygraph.stackPoints_(seriesPoints, cumulativeYval[axisIdx], seriesExtremes, this.getBooleanOption("stackedGraphNaNFill")); } extremes[seriesName] = seriesExtremes; points[seriesIdx] = seriesPoints;
                    } return { points: points, extremes: extremes, boundaryIds: boundaryIds };
                }; /**
 * Update the graph with new data. This method is called when the viewing area
 * has changed. If the underlying data or options have changed, predraw_ will
 * be called before drawGraph_ is called.
 *
 * @private
 */Dygraph.prototype.drawGraph_ = function () {
                    var start = new Date(); // This is used to set the second parameter to drawCallback, below.
                    var is_initial_draw = this.is_initial_draw_; this.is_initial_draw_ = false; this.layout_.removeAllDatasets(); this.setColors_(); this.attrs_.pointSize = 0.5 * this.getNumericOption('highlightCircleSize'); var packed = this.gatherDatasets_(this.rolledSeries_, this.dateWindow_); var points = packed.points; var extremes = packed.extremes; this.boundaryIds_ = packed.boundaryIds; this.setIndexByName_ = {}; var labels = this.attr_("labels"); var dataIdx = 0; for (var i = 1; i < points.length; i++) { if (!this.visibility()[i - 1]) continue; this.layout_.addDataset(labels[i], points[i]); this.datasetIndex_[i] = dataIdx++; } for (var i = 0; i < labels.length; i++) { this.setIndexByName_[labels[i]] = i; } this.computeYAxisRanges_(extremes); this.layout_.setYAxes(this.axes_); this.addXTicks_(); // Tell PlotKit to use this new data and render itself
                    this.layout_.evaluate(); this.renderGraph_(is_initial_draw); if (this.getStringOption("timingName")) { var end = new Date(); console.log(this.getStringOption("timingName") + " - drawGraph: " + (end - start) + "ms"); }
                }; /**
 * This does the work of drawing the chart. It assumes that the layout and axis
 * scales have already been set (e.g. by predraw_).
 *
 * @private
 */Dygraph.prototype.renderGraph_ = function (is_initial_draw) {
                    this.cascadeEvents_('clearChart'); this.plotter_.clear(); var underlayCallback = this.getFunctionOption('underlayCallback'); if (underlayCallback) { // NOTE: we pass the dygraph object to this callback twice to avoid breaking
                        // users who expect a deprecated form of this callback.
                        underlayCallback.call(this, this.hidden_ctx_, this.layout_.getPlotArea(), this, this);
                    } var e = { canvas: this.hidden_, drawingContext: this.hidden_ctx_ }; this.cascadeEvents_('willDrawChart', e); this.plotter_.render(); this.cascadeEvents_('didDrawChart', e); this.lastRow_ = -1; // because plugins/legend.js clears the legend
                    // TODO(danvk): is this a performance bottleneck when panning?
                    // The interaction canvas should already be empty in that situation.
                    this.canvas_.getContext('2d').clearRect(0, 0, this.width_, this.height_); var drawCallback = this.getFunctionOption("drawCallback"); if (drawCallback !== null) { drawCallback.call(this, this, is_initial_draw); } if (is_initial_draw) { this.readyFired_ = true; while (this.readyFns_.length > 0) { var fn = this.readyFns_.pop(); fn(this); } }
                }; /**
 * @private
 * Determine properties of the y-axes which are independent of the data
 * currently being displayed. This includes things like the number of axes and
 * the style of the axes. It does not include the range of each axis and its
 * tick marks.
 * This fills in this.axes_.
 * axes_ = [ { options } ]
 *   indices are into the axes_ array.
 */Dygraph.prototype.computeYAxes_ = function () {
                    var axis, index, opts, v; // this.axes_ doesn't match this.attributes_.axes_.options. It's used for
                    // data computation as well as options storage.
                    // Go through once and add all the axes.
                    this.axes_ = []; for (axis = 0; axis < this.attributes_.numAxes(); axis++) { // Add a new axis, making a copy of its per-axis options.
                        opts = { g: this }; utils.update(opts, this.attributes_.axisOptions(axis)); this.axes_[axis] = opts;
                    } for (axis = 0; axis < this.axes_.length; axis++) {
                        if (axis === 0) { opts = this.optionsViewForAxis_('y' + (axis ? '2' : '')); v = opts("valueRange"); if (v) this.axes_[axis].valueRange = v; } else { // To keep old behavior
                            var axes = this.user_attrs_.axes; if (axes && axes.y2) { v = axes.y2.valueRange; if (v) this.axes_[axis].valueRange = v; }
                        }
                    }
                }; /**
 * Returns the number of y-axes on the chart.
 * @return {number} the number of axes.
 */Dygraph.prototype.numAxes = function () { return this.attributes_.numAxes(); }; /**
 * @private
 * Returns axis properties for the given series.
 * @param {string} setName The name of the series for which to get axis
 * properties, e.g. 'Y1'.
 * @return {Object} The axis properties.
 */Dygraph.prototype.axisPropertiesForSeries = function (series) { // TODO(danvk): handle errors.
                    return this.axes_[this.attributes_.axisForSeries(series)];
                }; /**
 * @private
 * Determine the value range and tick marks for each axis.
 * @param {Object} extremes A mapping from seriesName -> [low, high]
 * This fills in the valueRange and ticks fields in each entry of this.axes_.
 */Dygraph.prototype.computeYAxisRanges_ = function (extremes) {
                    var isNullUndefinedOrNaN = function isNullUndefinedOrNaN(num) { return isNaN(parseFloat(num)); }; var numAxes = this.attributes_.numAxes(); var ypadCompat, span, series, ypad; var p_axis; // Compute extreme values, a span and tick marks for each axis.
                    for (var i = 0; i < numAxes; i++) {
                        var axis = this.axes_[i]; var logscale = this.attributes_.getForAxis("logscale", i); var includeZero = this.attributes_.getForAxis("includeZero", i); var independentTicks = this.attributes_.getForAxis("independentTicks", i); series = this.attributes_.seriesForAxis(i); // Add some padding. This supports two Y padding operation modes:
                        //
                        // - backwards compatible (yRangePad not set):
                        //   10% padding for automatic Y ranges, but not for user-supplied
                        //   ranges, and move a close-to-zero edge to zero, since drawing at the edge
                        //   results in invisible lines. Unfortunately lines drawn at the edge of a
                        //   user-supplied range will still be invisible. If logscale is
                        //   set, add a variable amount of padding at the top but
                        //   none at the bottom.
                        //
                        // - new-style (yRangePad set by the user):
                        //   always add the specified Y padding.
                        //
                        ypadCompat = true; ypad = 0.1; // add 10%
                        var yRangePad = this.getNumericOption('yRangePad'); if (yRangePad !== null) {
                            ypadCompat = false; // Convert pixel padding to ratio
                            ypad = yRangePad / this.plotter_.area.h;
                        } if (series.length === 0) { // If no series are defined or visible then use a reasonable default
                            axis.extremeRange = [0, 1];
                        } else { // Calculate the extremes of extremes.
                            var minY = Infinity; // extremes[series[0]][0];
                            var maxY = -Infinity; // extremes[series[0]][1];
                            var extremeMinY, extremeMaxY; for (var j = 0; j < series.length; j++) { // this skips invisible series
                                if (!extremes.hasOwnProperty(series[j])) continue; // Only use valid extremes to stop null data series' from corrupting the scale.
                                extremeMinY = extremes[series[j]][0]; if (extremeMinY !== null) { minY = Math.min(extremeMinY, minY); } extremeMaxY = extremes[series[j]][1]; if (extremeMaxY !== null) { maxY = Math.max(extremeMaxY, maxY); }
                            } // Include zero if requested by the user.
                            if (includeZero && !logscale) { if (minY > 0) minY = 0; if (maxY < 0) maxY = 0; } // Ensure we have a valid scale, otherwise default to [0, 1] for safety.
                            if (minY == Infinity) minY = 0; if (maxY == -Infinity) maxY = 1; span = maxY - minY; // special case: if we have no sense of scale, center on the sole value.
                            if (span === 0) {
                                if (maxY !== 0) { span = Math.abs(maxY); } else { // ... and if the sole value is zero, use range 0-1.
                                    maxY = 1; span = 1;
                                }
                            } var maxAxisY = maxY, minAxisY = minY; if (ypadCompat) {
                                if (logscale) { maxAxisY = maxY + ypad * span; minAxisY = minY; } else {
                                    maxAxisY = maxY + ypad * span; minAxisY = minY - ypad * span; // Backwards-compatible behavior: Move the span to start or end at zero if it's
                                    // close to zero.
                                    if (minAxisY < 0 && minY >= 0) minAxisY = 0; if (maxAxisY > 0 && maxY <= 0) maxAxisY = 0;
                                }
                            } axis.extremeRange = [minAxisY, maxAxisY];
                        } if (axis.valueRange) { // This is a user-set value range for this axis.
                            var y0 = isNullUndefinedOrNaN(axis.valueRange[0]) ? axis.extremeRange[0] : axis.valueRange[0]; var y1 = isNullUndefinedOrNaN(axis.valueRange[1]) ? axis.extremeRange[1] : axis.valueRange[1]; axis.computedValueRange = [y0, y1];
                        } else { axis.computedValueRange = axis.extremeRange; } if (!ypadCompat) { // When using yRangePad, adjust the upper/lower bounds to add
                            // padding unless the user has zoomed/panned the Y axis range.
                            if (logscale) { y0 = axis.computedValueRange[0]; y1 = axis.computedValueRange[1]; var y0pct = ypad / (2 * ypad - 1); var y1pct = (ypad - 1) / (2 * ypad - 1); axis.computedValueRange[0] = utils.logRangeFraction(y0, y1, y0pct); axis.computedValueRange[1] = utils.logRangeFraction(y0, y1, y1pct); } else { y0 = axis.computedValueRange[0]; y1 = axis.computedValueRange[1]; span = y1 - y0; axis.computedValueRange[0] = y0 - span * ypad; axis.computedValueRange[1] = y1 + span * ypad; }
                        } if (independentTicks) {
                        axis.independentTicks = independentTicks; var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); var ticker = opts('ticker'); axis.ticks = ticker(axis.computedValueRange[0], axis.computedValueRange[1], this.plotter_.area.h, opts, this); // Define the first independent axis as primary axis.
                            if (!p_axis) p_axis = axis;
                        }
                    } if (p_axis === undefined) { throw "Configuration Error: At least one axis has to have the \"independentTicks\" option activated."; } // Add ticks. By default, all axes inherit the tick positions of the
                    // primary axis. However, if an axis is specifically marked as having
                    // independent ticks, then that is permissible as well.
                    for (var i = 0; i < numAxes; i++) { var axis = this.axes_[i]; if (!axis.independentTicks) { var opts = this.optionsViewForAxis_('y' + (i ? '2' : '')); var ticker = opts('ticker'); var p_ticks = p_axis.ticks; var p_scale = p_axis.computedValueRange[1] - p_axis.computedValueRange[0]; var scale = axis.computedValueRange[1] - axis.computedValueRange[0]; var tick_values = []; for (var k = 0; k < p_ticks.length; k++) { var y_frac = (p_ticks[k].v - p_axis.computedValueRange[0]) / p_scale; var y_val = axis.computedValueRange[0] + y_frac * scale; tick_values.push(y_val); } axis.ticks = ticker(axis.computedValueRange[0], axis.computedValueRange[1], this.plotter_.area.h, opts, this, tick_values); } }
                }; /**
 * Detects the type of the str (date or numeric) and sets the various
 * formatting attributes in this.attrs_ based on this type.
 * @param {string} str An x value.
 * @private
 */Dygraph.prototype.detectTypeFromString_ = function (str) {
                    var isDate = false; var dashPos = str.indexOf('-'); // could be 2006-01-01 _or_ 1.0e-2
                    if (dashPos > 0 && str[dashPos - 1] != 'e' && str[dashPos - 1] != 'E' || str.indexOf('/') >= 0 || isNaN(parseFloat(str))) { isDate = true; } else if (str.length == 8 && str > '19700101' && str < '20371231') { // TODO(danvk): remove support for this format.
                        isDate = true;
                    } this.setXAxisOptions_(isDate);
                }; Dygraph.prototype.setXAxisOptions_ = function (isDate) {
                    if (isDate) { this.attrs_.xValueParser = utils.dateParser; this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter; this.attrs_.axes.x.ticker = DygraphTickers.dateTicker; this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; } else { /** @private (shut up, jsdoc!) */this.attrs_.xValueParser = function (x) { return parseFloat(x); }; // TODO(danvk): use Dygraph.numberValueFormatter here?
/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function (x) { return x; }; this.attrs_.axes.x.ticker = DygraphTickers.numericTicks; this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter;
                    }
                }; /**
 * @private
 * Parses a string in a special csv format.  We expect a csv file where each
 * line is a date point, and the first field in each line is the date string.
 * We also expect that all remaining fields represent series.
 * if the errorBars attribute is set, then interpret the fields as:
 * date, series1, stddev1, series2, stddev2, ...
 * @param {[Object]} data See above.
 *
 * @return [Object] An array with one entry for each row. These entries
 * are an array of cells in that row. The first entry is the parsed x-value for
 * the row. The second, third, etc. are the y-values. These can take on one of
 * three forms, depending on the CSV and constructor parameters:
 * 1. numeric value
 * 2. [ value, stddev ]
 * 3. [ low value, center value, high value ]
 */Dygraph.prototype.parseCSV_ = function (data) {
                    var ret = []; var line_delimiter = utils.detectLineDelimiter(data); var lines = data.split(line_delimiter || "\n"); var vals, j; // Use the default delimiter or fall back to a tab if that makes sense.
                    var delim = this.getStringOption('delimiter'); if (lines[0].indexOf(delim) == -1 && lines[0].indexOf('\t') >= 0) { delim = '\t'; } var start = 0; if (!('labels' in this.user_attrs_)) { // User hasn't explicitly set labels, so they're (presumably) in the CSV.
                        start = 1; this.attrs_.labels = lines[0].split(delim); // NOTE: _not_ user_attrs_.
                        this.attributes_.reparseSeries();
                    } var line_no = 0; var xParser; var defaultParserSet = false; // attempt to auto-detect x value type
                    var expectedCols = this.attr_("labels").length; var outOfOrder = false; for (var i = start; i < lines.length; i++) {
                        var line = lines[i]; line_no = i; if (line.length === 0) continue; // skip blank lines
                        if (line[0] == '#') continue; // skip comment lines
                        var inFields = line.split(delim); if (inFields.length < 2) continue; var fields = []; if (!defaultParserSet) { this.detectTypeFromString_(inFields[0]); xParser = this.getFunctionOption("xValueParser"); defaultParserSet = true; } fields[0] = xParser(inFields[0], this); // If fractions are expected, parse the numbers as "A/B"
                        if (this.fractions_) {
                            for (j = 1; j < inFields.length; j++) { // TODO(danvk): figure out an appropriate way to flag parse errors.
                                vals = inFields[j].split("/"); if (vals.length != 2) { console.error('Expected fractional "num/den" values in CSV data ' + "but found a value '" + inFields[j] + "' on line " + (1 + i) + " ('" + line + "') which is not of this form."); fields[j] = [0, 0]; } else { fields[j] = [utils.parseFloat_(vals[0], i, line), utils.parseFloat_(vals[1], i, line)]; }
                            }
                        } else if (this.getBooleanOption("errorBars")) { // If there are error bars, values are (value, stddev) pairs
                            if (inFields.length % 2 != 1) { console.error('Expected alternating (value, stdev.) pairs in CSV data ' + 'but line ' + (1 + i) + ' has an odd number of values (' + (inFields.length - 1) + "): '" + line + "'"); } for (j = 1; j < inFields.length; j += 2) { fields[(j + 1) / 2] = [utils.parseFloat_(inFields[j], i, line), utils.parseFloat_(inFields[j + 1], i, line)]; }
                        } else if (this.getBooleanOption("customBars")) { // Bars are a low;center;high tuple
                            for (j = 1; j < inFields.length; j++) { var val = inFields[j]; if (/^ *$/.test(val)) { fields[j] = [null, null, null]; } else { vals = val.split(";"); if (vals.length == 3) { fields[j] = [utils.parseFloat_(vals[0], i, line), utils.parseFloat_(vals[1], i, line), utils.parseFloat_(vals[2], i, line)]; } else { console.warn('When using customBars, values must be either blank ' + 'or "low;center;high" tuples (got "' + val + '" on line ' + (1 + i)); } } }
                        } else { // Values are just numbers
                            for (j = 1; j < inFields.length; j++) { fields[j] = utils.parseFloat_(inFields[j], i, line); }
                        } if (ret.length > 0 && fields[0] < ret[ret.length - 1][0]) { outOfOrder = true; } if (fields.length != expectedCols) { console.error("Number of columns in line " + i + " (" + fields.length + ") does not agree with number of labels (" + expectedCols + ") " + line); } // If the user specified the 'labels' option and none of the cells of the
                        // first row parsed correctly, then they probably double-specified the
                        // labels. We go with the values set in the option, discard this row and
                        // log a warning to the JS console.
                        if (i === 0 && this.attr_('labels')) { var all_null = true; for (j = 0; all_null && j < fields.length; j++) { if (fields[j]) all_null = false; } if (all_null) { console.warn("The dygraphs 'labels' option is set, but the first row " + "of CSV data ('" + line + "') appears to also contain " + "labels. Will drop the CSV labels and use the option " + "labels."); continue; } } ret.push(fields);
                    } if (outOfOrder) { console.warn("CSV is out of order; order it correctly to speed loading."); ret.sort(function (a, b) { return a[0] - b[0]; }); } return ret;
                }; // In native format, all values must be dates or numbers.
                // This check isn't perfect but will catch most mistaken uses of strings.
                function validateNativeFormat(data) {
                    var firstRow = data[0]; var firstX = firstRow[0]; if (typeof firstX !== 'number' && !utils.isDateLike(firstX)) { throw new Error('Expected number or date but got ' + typeof firstX + ': ' + firstX + '.'); } for (var i = 1; i < firstRow.length; i++) {
                        var val = firstRow[i]; if (val === null || val === undefined) continue; if (typeof val === 'number') continue; if (utils.isArrayLike(val)) continue; // e.g. error bars or custom bars.
                        throw new Error('Expected number or array but got ' + typeof val + ': ' + val + '.');
                    }
                } /**
 * The user has provided their data as a pre-packaged JS array. If the x values
 * are numeric, this is the same as dygraphs' internal format. If the x values
 * are dates, we need to convert them from Date objects to ms since epoch.
 * @param {!Array} data
 * @return {Object} data with numeric x values.
 * @private
 */Dygraph.prototype.parseArray_ = function (data) { // Peek at the first x value to see if it's numeric.
                    if (data.length === 0) { console.error("Can't plot empty data set"); return null; } if (data[0].length === 0) { console.error("Data set cannot contain an empty row"); return null; } validateNativeFormat(data); var i; if (this.attr_("labels") === null) {
                        console.warn("Using default labels. Set labels explicitly via 'labels' " + "in the options parameter"); this.attrs_.labels = ["X"]; for (i = 1; i < data[0].length; i++) {
                            this.attrs_.labels.push("Y" + i); // Not user_attrs_.
                        } this.attributes_.reparseSeries();
                    } else { var num_labels = this.attr_("labels"); if (num_labels.length != data[0].length) { console.error("Mismatch between number of labels (" + num_labels + ")" + " and number of columns in array (" + data[0].length + ")"); return null; } } if (utils.isDateLike(data[0][0])) { // Some intelligent defaults for a date x-axis.
                        this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter; this.attrs_.axes.x.ticker = DygraphTickers.dateTicker; this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; // Assume they're all dates.
                        var parsedData = utils.clone(data); for (i = 0; i < data.length; i++) { if (parsedData[i].length === 0) { console.error("Row " + (1 + i) + " of data is empty"); return null; } if (parsedData[i][0] === null || typeof parsedData[i][0].getTime != 'function' || isNaN(parsedData[i][0].getTime())) { console.error("x value in row " + (1 + i) + " is not a Date"); return null; } parsedData[i][0] = parsedData[i][0].getTime(); } return parsedData;
                    } else { // Some intelligent defaults for a numeric x-axis.
/** @private (shut up, jsdoc!) */this.attrs_.axes.x.valueFormatter = function (x) { return x; }; this.attrs_.axes.x.ticker = DygraphTickers.numericTicks; this.attrs_.axes.x.axisLabelFormatter = utils.numberAxisLabelFormatter; return data;
                    }
                }; /**
 * Parses a DataTable object from gviz.
 * The data is expected to have a first column that is either a date or a
 * number. All subsequent columns must be numbers. If there is a clear mismatch
 * between this.xValueParser_ and the type of the first column, it will be
 * fixed. Fills out rawData_.
 * @param {!google.visualization.DataTable} data See above.
 * @private
 */Dygraph.prototype.parseDataTable_ = function (data) {
                    var shortTextForAnnotationNum = function shortTextForAnnotationNum(num) { // converts [0-9]+ [A-Z][a-z]*
                        // example: 0=A, 1=B, 25=Z, 26=Aa, 27=Ab
                        // and continues like.. Ba Bb .. Za .. Zz..Aaa...Zzz Aaaa Zzzz
                        var shortText = String.fromCharCode(65 /* A */ + num % 26); num = Math.floor(num / 26); while (num > 0) { shortText = String.fromCharCode(65 /* A */ + (num - 1) % 26) + shortText.toLowerCase(); num = Math.floor((num - 1) / 26); } return shortText;
                    }; var cols = data.getNumberOfColumns(); var rows = data.getNumberOfRows(); var indepType = data.getColumnType(0); if (indepType == 'date' || indepType == 'datetime') { this.attrs_.xValueParser = utils.dateParser; this.attrs_.axes.x.valueFormatter = utils.dateValueFormatter; this.attrs_.axes.x.ticker = DygraphTickers.dateTicker; this.attrs_.axes.x.axisLabelFormatter = utils.dateAxisLabelFormatter; } else if (indepType == 'number') { this.attrs_.xValueParser = function (x) { return parseFloat(x); }; this.attrs_.axes.x.valueFormatter = function (x) { return x; }; this.attrs_.axes.x.ticker = DygraphTickers.numericTicks; this.attrs_.axes.x.axisLabelFormatter = this.attrs_.axes.x.valueFormatter; } else { throw new Error("only 'date', 'datetime' and 'number' types are supported " + "for column 1 of DataTable input (Got '" + indepType + "')"); } // Array of the column indices which contain data (and not annotations).
                    var colIdx = []; var annotationCols = {}; // data index -> [annotation cols]
                    var hasAnnotations = false; var i, j; for (i = 1; i < cols; i++) {
                        var type = data.getColumnType(i); if (type == 'number') { colIdx.push(i); } else if (type == 'string' && this.getBooleanOption('displayAnnotations')) { // This is OK -- it's an annotation column.
                            var dataIdx = colIdx[colIdx.length - 1]; if (!annotationCols.hasOwnProperty(dataIdx)) { annotationCols[dataIdx] = [i]; } else { annotationCols[dataIdx].push(i); } hasAnnotations = true;
                        } else { throw new Error("Only 'number' is supported as a dependent type with Gviz." + " 'string' is only supported if displayAnnotations is true"); }
                    } // Read column labels
                    // TODO(danvk): add support back for errorBars
                    var labels = [data.getColumnLabel(0)]; for (i = 0; i < colIdx.length; i++) { labels.push(data.getColumnLabel(colIdx[i])); if (this.getBooleanOption("errorBars")) i += 1; } this.attrs_.labels = labels; cols = labels.length; var ret = []; var outOfOrder = false; var annotations = []; for (i = 0; i < rows; i++) {
                        var row = []; if (typeof data.getValue(i, 0) === 'undefined' || data.getValue(i, 0) === null) { console.warn("Ignoring row " + i + " of DataTable because of undefined or null first column."); continue; } if (indepType == 'date' || indepType == 'datetime') { row.push(data.getValue(i, 0).getTime()); } else { row.push(data.getValue(i, 0)); } if (!this.getBooleanOption("errorBars")) {
                            for (j = 0; j < colIdx.length; j++) { var col = colIdx[j]; row.push(data.getValue(i, col)); if (hasAnnotations && annotationCols.hasOwnProperty(col) && data.getValue(i, annotationCols[col][0]) !== null) { var ann = {}; ann.series = data.getColumnLabel(col); ann.xval = row[0]; ann.shortText = shortTextForAnnotationNum(annotations.length); ann.text = ''; for (var k = 0; k < annotationCols[col].length; k++) { if (k) ann.text += "\n"; ann.text += data.getValue(i, annotationCols[col][k]); } annotations.push(ann); } } // Strip out infinities, which give dygraphs problems later on.
                            for (j = 0; j < row.length; j++) { if (!isFinite(row[j])) row[j] = null; }
                        } else { for (j = 0; j < cols - 1; j++) { row.push([data.getValue(i, 1 + 2 * j), data.getValue(i, 2 + 2 * j)]); } } if (ret.length > 0 && row[0] < ret[ret.length - 1][0]) { outOfOrder = true; } ret.push(row);
                    } if (outOfOrder) { console.warn("DataTable is out of order; order it correctly to speed loading."); ret.sort(function (a, b) { return a[0] - b[0]; }); } this.rawData_ = ret; if (annotations.length > 0) { this.setAnnotations(annotations, true); } this.attributes_.reparseSeries();
                }; /**
 * Signals to plugins that the chart data has updated.
 * This happens after the data has updated but before the chart has redrawn.
 * @private
 */Dygraph.prototype.cascadeDataDidUpdateEvent_ = function () { // TODO(danvk): there are some issues checking xAxisRange() and using
                    // toDomCoords from handlers of this event. The visible range should be set
                    // when the chart is drawn, not derived from the data.
                    this.cascadeEvents_('dataDidUpdate', {});
                }; /**
 * Get the CSV data. If it's in a function, call that function. If it's in a
 * file, do an XMLHttpRequest to get it.
 * @private
 */Dygraph.prototype.start_ = function () {
                    var data = this.file_; // Functions can return references of all other types.
                    if (typeof data == 'function') { data = data(); } if (utils.isArrayLike(data)) { this.rawData_ = this.parseArray_(data); this.cascadeDataDidUpdateEvent_(); this.predraw_(); } else if (typeof data == 'object' && typeof data.getColumnRange == 'function') { // must be a DataTable from gviz.
                        this.parseDataTable_(data); this.cascadeDataDidUpdateEvent_(); this.predraw_();
                    } else if (typeof data == 'string') { // Heuristic: a newline means it's CSV data. Otherwise it's an URL.
                        var line_delimiter = utils.detectLineDelimiter(data); if (line_delimiter) { this.loadedEvent_(data); } else { // REMOVE_FOR_IE
                            var req; if (window.XMLHttpRequest) { // Firefox, Opera, IE7, and other browsers will use the native object
                                req = new XMLHttpRequest();
                            } else { // IE 5 and 6 will use the ActiveX control
                                req = new ActiveXObject("Microsoft.XMLHTTP");
                            } var caller = this; req.onreadystatechange = function () {
                                if (req.readyState == 4) {
                                    if (req.status === 200 ||  // Normal http
                                        req.status === 0) { // Chrome w/ --allow-file-access-from-files
                                        caller.loadedEvent_(req.responseText);
                                    }
                                }
                            }; req.open("GET", data, true); req.send(null);
                        }
                    } else { console.error("Unknown data format: " + typeof data); }
                }; /**
 * Changes various properties of the graph. These can include:
 * <ul>
 * <li>file: changes the source data for the graph</li>
 * <li>errorBars: changes whether the data contains stddev</li>
 * </ul>
 *
 * There's a huge variety of options that can be passed to this method. For a
 * full list, see http://dygraphs.com/options.html.
 *
 * @param {Object} input_attrs The new properties and values
 * @param {boolean} block_redraw Usually the chart is redrawn after every
 *     call to updateOptions(). If you know better, you can pass true to
 *     explicitly block the redraw. This can be useful for chaining
 *     updateOptions() calls, avoiding the occasional infinite loop and
 *     preventing redraws when it's not necessary (e.g. when updating a
 *     callback).
 */Dygraph.prototype.updateOptions = function (input_attrs, block_redraw) {
                    if (typeof block_redraw == 'undefined') block_redraw = false; // copyUserAttrs_ drops the "file" parameter as a convenience to us.
                    var file = input_attrs.file; var attrs = Dygraph.copyUserAttrs_(input_attrs); // TODO(danvk): this is a mess. Move these options into attr_.
                    if ('rollPeriod' in attrs) { this.rollPeriod_ = attrs.rollPeriod; } if ('dateWindow' in attrs) { this.dateWindow_ = attrs.dateWindow; } // TODO(danvk): validate per-series options.
                    // Supported:
                    // strokeWidth
                    // pointSize
                    // drawPoints
                    // highlightCircleSize
                    // Check if this set options will require new points.
                    var requiresNewPoints = utils.isPixelChangingOptionList(this.attr_("labels"), attrs); utils.updateDeep(this.user_attrs_, attrs); this.attributes_.reparseSeries(); if (file) { // This event indicates that the data is about to change, but hasn't yet.
                        // TODO(danvk): support cancellation of the update via this event.
                        this.cascadeEvents_('dataWillUpdate', {}); this.file_ = file; if (!block_redraw) this.start_();
                    } else { if (!block_redraw) { if (requiresNewPoints) { this.predraw_(); } else { this.renderGraph_(false); } } }
                }; /**
 * Make a copy of input attributes, removing file as a convenience.
 * @private
 */Dygraph.copyUserAttrs_ = function (attrs) { var my_attrs = {}; for (var k in attrs) { if (!attrs.hasOwnProperty(k)) continue; if (k == 'file') continue; if (attrs.hasOwnProperty(k)) my_attrs[k] = attrs[k]; } return my_attrs; }; /**
 * Resizes the dygraph. If no parameters are specified, resizes to fill the
 * containing div (which has presumably changed size since the dygraph was
 * instantiated. If the width/height are specified, the div will be resized.
 *
 * This is far more efficient than destroying and re-instantiating a
 * Dygraph, since it doesn't have to reparse the underlying data.
 *
 * @param {number} width Width (in pixels)
 * @param {number} height Height (in pixels)
 */Dygraph.prototype.resize = function (width, height) {
                    if (this.resize_lock) { return; } this.resize_lock = true; if (width === null != (height === null)) { console.warn("Dygraph.resize() should be called with zero parameters or " + "two non-NULL parameters. Pretending it was zero."); width = height = null; } var old_width = this.width_; var old_height = this.height_; if (width) { this.maindiv_.style.width = width + "px"; this.maindiv_.style.height = height + "px"; this.width_ = width; this.height_ = height; } else { this.width_ = this.maindiv_.clientWidth; this.height_ = this.maindiv_.clientHeight; } if (old_width != this.width_ || old_height != this.height_) { // Resizing a canvas erases it, even when the size doesn't change, so
                        // any resize needs to be followed by a redraw.
                        this.resizeElements_(); this.predraw_();
                    } this.resize_lock = false;
                }; /**
 * Adjusts the number of points in the rolling average. Updates the graph to
 * reflect the new averaging period.
 * @param {number} length Number of points over which to average the data.
 */Dygraph.prototype.adjustRoll = function (length) { this.rollPeriod_ = length; this.predraw_(); }; /**
 * Returns a boolean array of visibility statuses.
 */Dygraph.prototype.visibility = function () { // Do lazy-initialization, so that this happens after we know the number of
                    // data series.
                    if (!this.getOption("visibility")) { this.attrs_.visibility = []; } // TODO(danvk): it looks like this could go into an infinite loop w/ user_attrs.
                    while (this.getOption("visibility").length < this.numColumns() - 1) { this.attrs_.visibility.push(true); } return this.getOption("visibility");
                }; /**
 * Changes the visibility of one or more series.
 *
 * @param {number|number[]|object} num the series index or an array of series indices
 *                                     or a boolean array of visibility states by index
 *                                     or an object mapping series numbers, as keys, to
 *                                     visibility state (boolean values)
 * @param {boolean} value the visibility state expressed as a boolean
 */Dygraph.prototype.setVisibility = function (num, value) { var x = this.visibility(); var numIsObject = false; if (!Array.isArray(num)) { if (num !== null && typeof num === 'object') { numIsObject = true; } else { num = [num]; } } if (numIsObject) { for (var i in num) { if (num.hasOwnProperty(i)) { if (i < 0 || i >= x.length) { console.warn("Invalid series number in setVisibility: " + i); } else { x[i] = num[i]; } } } } else { for (var i = 0; i < num.length; i++) { if (typeof num[i] === 'boolean') { if (i >= x.length) { console.warn("Invalid series number in setVisibility: " + i); } else { x[i] = num[i]; } } else { if (num[i] < 0 || num[i] >= x.length) { console.warn("Invalid series number in setVisibility: " + num[i]); } else { x[num[i]] = value; } } } } this.predraw_(); }; /**
 * How large of an area will the dygraph render itself in?
 * This is used for testing.
 * @return A {width: w, height: h} object.
 * @private
 */Dygraph.prototype.size = function () { return { width: this.width_, height: this.height_ }; }; /**
 * Update the list of annotations and redraw the chart.
 * See dygraphs.com/annotations.html for more info on how to use annotations.
 * @param ann {Array} An array of annotation objects.
 * @param suppressDraw {Boolean} Set to "true" to block chart redraw (optional).
 */Dygraph.prototype.setAnnotations = function (ann, suppressDraw) { // Only add the annotation CSS rule once we know it will be used.
                    this.annotations_ = ann; if (!this.layout_) { console.warn("Tried to setAnnotations before dygraph was ready. " + "Try setting them in a ready() block. See " + "dygraphs.com/tests/annotation.html"); return; } this.layout_.setAnnotations(this.annotations_); if (!suppressDraw) { this.predraw_(); }
                }; /**
 * Return the list of annotations.
 */Dygraph.prototype.annotations = function () { return this.annotations_; }; /**
 * Get the list of label names for this graph. The first column is the
 * x-axis, so the data series names start at index 1.
 *
 * Returns null when labels have not yet been defined.
 */Dygraph.prototype.getLabels = function () { var labels = this.attr_("labels"); return labels ? labels.slice() : null; }; /**
 * Get the index of a series (column) given its name. The first column is the
 * x-axis, so the data series start with index 1.
 */Dygraph.prototype.indexFromSetName = function (name) { return this.setIndexByName_[name]; }; /**
 * Find the row number corresponding to the given x-value.
 * Returns null if there is no such x-value in the data.
 * If there are multiple rows with the same x-value, this will return the
 * first one.
 * @param {number} xVal The x-value to look for (e.g. millis since epoch).
 * @return {?number} The row number, which you can pass to getValue(), or null.
 */Dygraph.prototype.getRowForX = function (xVal) {
                    var low = 0, high = this.numRows() - 1; while (low <= high) {
                        var idx = high + low >> 1; var x = this.getValue(idx, 0); if (x < xVal) { low = idx + 1; } else if (x > xVal) { high = idx - 1; } else if (low != idx) { // equal, but there may be an earlier match.
                            high = idx;
                        } else { return idx; }
                    } return null;
                }; /**
 * Trigger a callback when the dygraph has drawn itself and is ready to be
 * manipulated. This is primarily useful when dygraphs has to do an XHR for the
 * data (i.e. a URL is passed as the data source) and the chart is drawn
 * asynchronously. If the chart has already drawn, the callback will fire
 * immediately.
 *
 * This is a good place to call setAnnotation().
 *
 * @param {function(!Dygraph)} callback The callback to trigger when the chart
 *     is ready.
 */Dygraph.prototype.ready = function (callback) { if (this.is_initial_draw_) { this.readyFns_.push(callback); } else { callback.call(this, this); } }; /**
 * Add an event handler. This event handler is kept until the graph is
 * destroyed with a call to graph.destroy().
 *
 * @param {!Node} elem The element to add the event to.
 * @param {string} type The type of the event, e.g. 'click' or 'mousemove'.
 * @param {function(Event):(boolean|undefined)} fn The function to call
 *     on the event. The function takes one parameter: the event object.
 * @private
 */Dygraph.prototype.addAndTrackEvent = function (elem, type, fn) { utils.addEvent(elem, type, fn); this.registeredEvents_.push({ elem: elem, type: type, fn: fn }); }; Dygraph.prototype.removeTrackedEvents_ = function () { if (this.registeredEvents_) { for (var idx = 0; idx < this.registeredEvents_.length; idx++) { var reg = this.registeredEvents_[idx]; utils.removeEvent(reg.elem, reg.type, reg.fn); } } this.registeredEvents_ = []; }; // Installed plugins, in order of precedence (most-general to most-specific).
                Dygraph.PLUGINS = [_pluginsLegend2['default'], _pluginsAxes2['default'], _pluginsRangeSelector2['default'], // Has to be before ChartLabels so that its callbacks are called after ChartLabels' callbacks.
                _pluginsChartLabels2['default'], _pluginsAnnotations2['default'], _pluginsGrid2['default']]; // There are many symbols which have historically been available through the
                // Dygraph class. These are exported here for backwards compatibility.
                Dygraph.GVizChart = _dygraphGviz2['default']; Dygraph.DASHED_LINE = utils.DASHED_LINE; Dygraph.DOT_DASH_LINE = utils.DOT_DASH_LINE; Dygraph.dateAxisLabelFormatter = utils.dateAxisLabelFormatter; Dygraph.toRGB_ = utils.toRGB_; Dygraph.findPos = utils.findPos; Dygraph.pageX = utils.pageX; Dygraph.pageY = utils.pageY; Dygraph.dateString_ = utils.dateString_; Dygraph.defaultInteractionModel = _dygraphInteractionModel2['default'].defaultModel; Dygraph.nonInteractiveModel = Dygraph.nonInteractiveModel_ = _dygraphInteractionModel2['default'].nonInteractiveModel_; Dygraph.Circles = utils.Circles; Dygraph.Plugins = { Legend: _pluginsLegend2['default'], Axes: _pluginsAxes2['default'], Annotations: _pluginsAnnotations2['default'], ChartLabels: _pluginsChartLabels2['default'], Grid: _pluginsGrid2['default'], RangeSelector: _pluginsRangeSelector2['default'] }; Dygraph.DataHandlers = { DefaultHandler: _datahandlerDefault2['default'], BarsHandler: _datahandlerBars2['default'], CustomBarsHandler: _datahandlerBarsCustom2['default'], DefaultFractionHandler: _datahandlerDefaultFractions2['default'], ErrorBarsHandler: _datahandlerBarsError2['default'], FractionsBarsHandler: _datahandlerBarsFractions2['default'] }; Dygraph.startPan = _dygraphInteractionModel2['default'].startPan; Dygraph.startZoom = _dygraphInteractionModel2['default'].startZoom; Dygraph.movePan = _dygraphInteractionModel2['default'].movePan; Dygraph.moveZoom = _dygraphInteractionModel2['default'].moveZoom; Dygraph.endPan = _dygraphInteractionModel2['default'].endPan; Dygraph.endZoom = _dygraphInteractionModel2['default'].endZoom; Dygraph.numericLinearTicks = DygraphTickers.numericLinearTicks; Dygraph.numericTicks = DygraphTickers.numericTicks; Dygraph.dateTicker = DygraphTickers.dateTicker; Dygraph.Granularity = DygraphTickers.Granularity; Dygraph.getDateAxis = DygraphTickers.getDateAxis; Dygraph.floatFormat = utils.floatFormat; exports['default'] = Dygraph; module.exports = exports['default'];

            }).call(this, require('_process'))

        }, { "./datahandler/bars": 5, "./datahandler/bars-custom": 2, "./datahandler/bars-error": 3, "./datahandler/bars-fractions": 4, "./datahandler/default": 8, "./datahandler/default-fractions": 7, "./dygraph-canvas": 9, "./dygraph-default-attrs": 10, "./dygraph-gviz": 11, "./dygraph-interaction-model": 12, "./dygraph-layout": 13, "./dygraph-options": 15, "./dygraph-options-reference": 14, "./dygraph-tickers": 16, "./dygraph-utils": 17, "./iframe-tarp": 19, "./plugins/annotations": 20, "./plugins/axes": 21, "./plugins/chart-labels": 22, "./plugins/grid": 23, "./plugins/legend": 24, "./plugins/range-selector": 25, "_process": 1 }], 19: [function (require, module, exports) {
            /**
             * To create a "drag" interaction, you typically register a mousedown event
             * handler on the element where the drag begins. In that handler, you register a
             * mouseup handler on the window to determine when the mouse is released,
             * wherever that release happens. This works well, except when the user releases
             * the mouse over an off-domain iframe. In that case, the mouseup event is
             * handled by the iframe and never bubbles up to the window handler.
             *
             * To deal with this issue, we cover iframes with high z-index divs to make sure
             * they don't capture mouseup.
             *
             * Usage:
             * element.addEventListener('mousedown', function() {
             *   var tarper = new IFrameTarp();
             *   tarper.cover();
             *   var mouseUpHandler = function() {
             *     ...
             *     window.removeEventListener(mouseUpHandler);
             *     tarper.uncover();
             *   };
             *   window.addEventListener('mouseup', mouseUpHandler);
             * };
             *
             * @constructor
             */
            "use strict";

            Object.defineProperty(exports, "__esModule", {
                value: true
            });

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }

            var _dygraphUtils = require('./dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            function IFrameTarp() {
                /** @type {Array.<!HTMLDivElement>} */
                this.tarps = [];
            };

            /**
             * Find all the iframes in the document and cover them with high z-index
             * transparent divs.
             */
            IFrameTarp.prototype.cover = function () {
                var iframes = document.getElementsByTagName("iframe");
                for (var i = 0; i < iframes.length; i++) {
                    var iframe = iframes[i];
                    var pos = utils.findPos(iframe),
                        x = pos.x,
                        y = pos.y,
                        width = iframe.offsetWidth,
                        height = iframe.offsetHeight;

                    var div = document.createElement("div");
                    div.style.position = "absolute";
                    div.style.left = x + 'px';
                    div.style.top = y + 'px';
                    div.style.width = width + 'px';
                    div.style.height = height + 'px';
                    div.style.zIndex = 999;
                    document.body.appendChild(div);
                    this.tarps.push(div);
                }
            };

            /**
             * Remove all the iframe covers. You should call this in a mouseup handler.
             */
            IFrameTarp.prototype.uncover = function () {
                for (var i = 0; i < this.tarps.length; i++) {
                    this.tarps[i].parentNode.removeChild(this.tarps[i]);
                }
                this.tarps = [];
            };

            exports["default"] = IFrameTarp;
            module.exports = exports["default"];

        }, { "./dygraph-utils": 17 }], 20: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /*global Dygraph:false */

            "use strict";

            /**
            Current bits of jankiness:
            - Uses dygraph.layout_ to get the parsed annotations.
            - Uses dygraph.plotter_.area
            
            It would be nice if the plugin didn't require so much special support inside
            the core dygraphs classes, but annotations involve quite a bit of parsing and
            layout.
            
            TODO(danvk): cache DOM elements.
            */

            Object.defineProperty(exports, "__esModule", {
                value: true
            });
            var annotations = function annotations() {
                this.annotations_ = [];
            };

            annotations.prototype.toString = function () {
                return "Annotations Plugin";
            };

            annotations.prototype.activate = function (g) {
                return {
                    clearChart: this.clearChart,
                    didDrawChart: this.didDrawChart
                };
            };

            annotations.prototype.detachLabels = function () {
                for (var i = 0; i < this.annotations_.length; i++) {
                    var a = this.annotations_[i];
                    if (a.parentNode) a.parentNode.removeChild(a);
                    this.annotations_[i] = null;
                }
                this.annotations_ = [];
            };

            annotations.prototype.clearChart = function (e) {
                this.detachLabels();
            };

            annotations.prototype.didDrawChart = function (e) {
                var g = e.dygraph;

                // Early out in the (common) case of zero annotations.
                var points = g.layout_.annotated_points;
                if (!points || points.length === 0) return;

                var containerDiv = e.canvas.parentNode;

                var bindEvt = function bindEvt(eventName, classEventName, pt) {
                    return function (annotation_event) {
                        var a = pt.annotation;
                        if (a.hasOwnProperty(eventName)) {
                            a[eventName](a, pt, g, annotation_event);
                        } else if (g.getOption(classEventName)) {
                            g.getOption(classEventName)(a, pt, g, annotation_event);
                        }
                    };
                };

                // Add the annotations one-by-one.
                var area = e.dygraph.getArea();

                // x-coord to sum of previous annotation's heights (used for stacking).
                var xToUsedHeight = {};

                for (var i = 0; i < points.length; i++) {
                    var p = points[i];
                    if (p.canvasx < area.x || p.canvasx > area.x + area.w || p.canvasy < area.y || p.canvasy > area.y + area.h) {
                        continue;
                    }

                    var a = p.annotation;
                    var tick_height = 6;
                    if (a.hasOwnProperty("tickHeight")) {
                        tick_height = a.tickHeight;
                    }

                    // TODO: deprecate axisLabelFontSize in favor of CSS
                    var div = document.createElement("div");
                    div.style['fontSize'] = g.getOption('axisLabelFontSize') + "px";
                    var className = 'dygraph-annotation';
                    if (!a.hasOwnProperty('icon')) {
                        // camelCase class names are deprecated.
                        className += ' dygraphDefaultAnnotation dygraph-default-annotation';
                    }
                    if (a.hasOwnProperty('cssClass')) {
                        className += " " + a.cssClass;
                    }
                    div.className = className;

                    var width = a.hasOwnProperty('width') ? a.width : 16;
                    var height = a.hasOwnProperty('height') ? a.height : 16;
                    if (a.hasOwnProperty('icon')) {
                        var img = document.createElement("img");
                        img.src = a.icon;
                        img.width = width;
                        img.height = height;
                        div.appendChild(img);
                    } else if (p.annotation.hasOwnProperty('shortText')) {
                        div.appendChild(document.createTextNode(p.annotation.shortText));
                    }
                    var left = p.canvasx - width / 2;
                    div.style.left = left + "px";
                    var divTop = 0;
                    if (a.attachAtBottom) {
                        var y = area.y + area.h - height - tick_height;
                        if (xToUsedHeight[left]) {
                            y -= xToUsedHeight[left];
                        } else {
                            xToUsedHeight[left] = 0;
                        }
                        xToUsedHeight[left] += tick_height + height;
                        divTop = y;
                    } else {
                        divTop = p.canvasy - height - tick_height;
                    }
                    div.style.top = divTop + "px";
                    div.style.width = width + "px";
                    div.style.height = height + "px";
                    div.title = p.annotation.text;
                    div.style.color = g.colorsMap_[p.name];
                    div.style.borderColor = g.colorsMap_[p.name];
                    a.div = div;

                    g.addAndTrackEvent(div, 'click', bindEvt('clickHandler', 'annotationClickHandler', p, this));
                    g.addAndTrackEvent(div, 'mouseover', bindEvt('mouseOverHandler', 'annotationMouseOverHandler', p, this));
                    g.addAndTrackEvent(div, 'mouseout', bindEvt('mouseOutHandler', 'annotationMouseOutHandler', p, this));
                    g.addAndTrackEvent(div, 'dblclick', bindEvt('dblClickHandler', 'annotationDblClickHandler', p, this));

                    containerDiv.appendChild(div);
                    this.annotations_.push(div);

                    var ctx = e.drawingContext;
                    ctx.save();
                    ctx.strokeStyle = a.hasOwnProperty('tickColor') ? a.tickColor : g.colorsMap_[p.name];
                    ctx.lineWidth = a.hasOwnProperty('tickWidth') ? a.tickWidth : g.getOption('strokeWidth');
                    ctx.beginPath();
                    if (!a.attachAtBottom) {
                        ctx.moveTo(p.canvasx, p.canvasy);
                        ctx.lineTo(p.canvasx, p.canvasy - 2 - tick_height);
                    } else {
                        var y = divTop + height;
                        ctx.moveTo(p.canvasx, y);
                        ctx.lineTo(p.canvasx, y + tick_height);
                    }
                    ctx.closePath();
                    ctx.stroke();
                    ctx.restore();
                }
            };

            annotations.prototype.destroy = function () {
                this.detachLabels();
            };

            exports["default"] = annotations;
            module.exports = exports["default"];

        }, {}], 21: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */

            /*global Dygraph:false */

            'use strict';

            /*
            Bits of jankiness:
            - Direct layout access
            - Direct area access
            - Should include calculation of ticks, not just the drawing.
            
            Options left to make axis-friendly.
              ('drawAxesAtZero')
              ('xAxisHeight')
            */

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

            var _dygraphUtils = require('../dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            /**
             * Draws the axes. This includes the labels on the x- and y-axes, as well
             * as the tick marks on the axes.
             * It does _not_ draw the grid lines which span the entire chart.
             */
            var axes = function axes() {
                this.xlabels_ = [];
                this.ylabels_ = [];
            };

            axes.prototype.toString = function () {
                return 'Axes Plugin';
            };

            axes.prototype.activate = function (g) {
                return {
                    layout: this.layout,
                    clearChart: this.clearChart,
                    willDrawChart: this.willDrawChart
                };
            };

            axes.prototype.layout = function (e) {
                var g = e.dygraph;

                if (g.getOptionForAxis('drawAxis', 'y')) {
                    var w = g.getOptionForAxis('axisLabelWidth', 'y') + 2 * g.getOptionForAxis('axisTickSize', 'y');
                    e.reserveSpaceLeft(w);
                }

                if (g.getOptionForAxis('drawAxis', 'x')) {
                    var h;
                    // NOTE: I think this is probably broken now, since g.getOption() now
                    // hits the dictionary. (That is, g.getOption('xAxisHeight') now always
                    // has a value.)
                    if (g.getOption('xAxisHeight')) {
                        h = g.getOption('xAxisHeight');
                    } else {
                        h = g.getOptionForAxis('axisLabelFontSize', 'x') + 2 * g.getOptionForAxis('axisTickSize', 'x');
                    }
                    e.reserveSpaceBottom(h);
                }

                if (g.numAxes() == 2) {
                    if (g.getOptionForAxis('drawAxis', 'y2')) {
                        var w = g.getOptionForAxis('axisLabelWidth', 'y2') + 2 * g.getOptionForAxis('axisTickSize', 'y2');
                        e.reserveSpaceRight(w);
                    }
                } else if (g.numAxes() > 2) {
                    g.error('Only two y-axes are supported at this time. (Trying ' + 'to use ' + g.numAxes() + ')');
                }
            };

            axes.prototype.detachLabels = function () {
                function removeArray(ary) {
                    for (var i = 0; i < ary.length; i++) {
                        var el = ary[i];
                        if (el.parentNode) el.parentNode.removeChild(el);
                    }
                }

                removeArray(this.xlabels_);
                removeArray(this.ylabels_);
                this.xlabels_ = [];
                this.ylabels_ = [];
            };

            axes.prototype.clearChart = function (e) {
                this.detachLabels();
            };

            axes.prototype.willDrawChart = function (e) {
                var _this = this;

                var g = e.dygraph;

                if (!g.getOptionForAxis('drawAxis', 'x') && !g.getOptionForAxis('drawAxis', 'y') && !g.getOptionForAxis('drawAxis', 'y2')) {
                    return;
                }

                // Round pixels to half-integer boundaries for crisper drawing.
                function halfUp(x) {
                    return Math.round(x) + 0.5;
                }
                function halfDown(y) {
                    return Math.round(y) - 0.5;
                }

                var context = e.drawingContext;
                var containerDiv = e.canvas.parentNode;
                var canvasWidth = g.width_; // e.canvas.width is affected by pixel ratio.
                var canvasHeight = g.height_;

                var label, x, y, tick, i;

                var makeLabelStyle = function makeLabelStyle(axis) {
                    return {
                        position: 'absolute',
                        fontSize: g.getOptionForAxis('axisLabelFontSize', axis) + 'px',
                        width: g.getOptionForAxis('axisLabelWidth', axis) + 'px'
                    };
                };

                var labelStyles = {
                    x: makeLabelStyle('x'),
                    y: makeLabelStyle('y'),
                    y2: makeLabelStyle('y2')
                };

                var makeDiv = function makeDiv(txt, axis, prec_axis) {
                    /*
                     * This seems to be called with the following three sets of axis/prec_axis:
                     * x: undefined
                     * y: y1
                     * y: y2
                     */
                    var div = document.createElement('div');
                    var labelStyle = labelStyles[prec_axis == 'y2' ? 'y2' : axis];
                    utils.update(div.style, labelStyle);
                    // TODO: combine outer & inner divs
                    var inner_div = document.createElement('div');
                    inner_div.className = 'dygraph-axis-label' + ' dygraph-axis-label-' + axis + (prec_axis ? ' dygraph-axis-label-' + prec_axis : '');
                    inner_div.innerHTML = txt;
                    div.appendChild(inner_div);
                    return div;
                };

                // axis lines
                context.save();

                var layout = g.layout_;
                var area = e.dygraph.plotter_.area;

                // Helper for repeated axis-option accesses.
                var makeOptionGetter = function makeOptionGetter(axis) {
                    return function (option) {
                        return g.getOptionForAxis(option, axis);
                    };
                };

                if (g.getOptionForAxis('drawAxis', 'y')) {
                    if (layout.yticks && layout.yticks.length > 0) {
                        var num_axes = g.numAxes();
                        var getOptions = [makeOptionGetter('y'), makeOptionGetter('y2')];
                        layout.yticks.forEach(function (tick) {
                            if (tick.label === undefined) return; // this tick only has a grid line.
                            x = area.x;
                            var sgn = 1;
                            var prec_axis = 'y1';
                            var getAxisOption = getOptions[0];
                            if (tick.axis == 1) {
                                // right-side y-axis
                                x = area.x + area.w;
                                sgn = -1;
                                prec_axis = 'y2';
                                getAxisOption = getOptions[1];
                            }
                            var fontSize = getAxisOption('axisLabelFontSize');
                            y = area.y + tick.pos * area.h;

                            /* Tick marks are currently clipped, so don't bother drawing them.
                            context.beginPath();
                            context.moveTo(halfUp(x), halfDown(y));
                            context.lineTo(halfUp(x - sgn * this.attr_('axisTickSize')), halfDown(y));
                            context.closePath();
                            context.stroke();
                            */

                            label = makeDiv(tick.label, 'y', num_axes == 2 ? prec_axis : null);
                            var top = y - fontSize / 2;
                            if (top < 0) top = 0;

                            if (top + fontSize + 3 > canvasHeight) {
                                label.style.bottom = '0';
                            } else {
                                label.style.top = top + 'px';
                            }
                            // TODO: replace these with css classes?
                            if (tick.axis === 0) {
                                label.style.left = area.x - getAxisOption('axisLabelWidth') - getAxisOption('axisTickSize') + 'px';
                                label.style.textAlign = 'right';
                            } else if (tick.axis == 1) {
                                label.style.left = area.x + area.w + getAxisOption('axisTickSize') + 'px';
                                label.style.textAlign = 'left';
                            }
                            label.style.width = getAxisOption('axisLabelWidth') + 'px';
                            containerDiv.appendChild(label);
                            _this.ylabels_.push(label);
                        });

                        // The lowest tick on the y-axis often overlaps with the leftmost
                        // tick on the x-axis. Shift the bottom tick up a little bit to
                        // compensate if necessary.
                        var bottomTick = this.ylabels_[0];
                        // Interested in the y2 axis also?
                        var fontSize = g.getOptionForAxis('axisLabelFontSize', 'y');
                        var bottom = parseInt(bottomTick.style.top, 10) + fontSize;
                        if (bottom > canvasHeight - fontSize) {
                            bottomTick.style.top = parseInt(bottomTick.style.top, 10) - fontSize / 2 + 'px';
                        }
                    }

                    // draw a vertical line on the left to separate the chart from the labels.
                    var axisX;
                    if (g.getOption('drawAxesAtZero')) {
                        var r = g.toPercentXCoord(0);
                        if (r > 1 || r < 0 || isNaN(r)) r = 0;
                        axisX = halfUp(area.x + r * area.w);
                    } else {
                        axisX = halfUp(area.x);
                    }

                    context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y');
                    context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y');

                    context.beginPath();
                    context.moveTo(axisX, halfDown(area.y));
                    context.lineTo(axisX, halfDown(area.y + area.h));
                    context.closePath();
                    context.stroke();

                    // if there's a secondary y-axis, draw a vertical line for that, too.
                    if (g.numAxes() == 2) {
                        context.strokeStyle = g.getOptionForAxis('axisLineColor', 'y2');
                        context.lineWidth = g.getOptionForAxis('axisLineWidth', 'y2');
                        context.beginPath();
                        context.moveTo(halfDown(area.x + area.w), halfDown(area.y));
                        context.lineTo(halfDown(area.x + area.w), halfDown(area.y + area.h));
                        context.closePath();
                        context.stroke();
                    }
                }

                if (g.getOptionForAxis('drawAxis', 'x')) {
                    if (layout.xticks) {
                        var getAxisOption = makeOptionGetter('x');
                        layout.xticks.forEach(function (tick) {
                            if (tick.label === undefined) return; // this tick only has a grid line.
                            x = area.x + tick.pos * area.w;
                            y = area.y + area.h;

                            /* Tick marks are currently clipped, so don't bother drawing them.
                            context.beginPath();
                            context.moveTo(halfUp(x), halfDown(y));
                            context.lineTo(halfUp(x), halfDown(y + this.attr_('axisTickSize')));
                            context.closePath();
                            context.stroke();
                            */

                            label = makeDiv(tick.label, 'x');
                            label.style.textAlign = 'center';
                            label.style.top = y + getAxisOption('axisTickSize') + 'px';

                            var left = x - getAxisOption('axisLabelWidth') / 2;
                            if (left + getAxisOption('axisLabelWidth') > canvasWidth) {
                                left = canvasWidth - getAxisOption('axisLabelWidth');
                                label.style.textAlign = 'right';
                            }
                            if (left < 0) {
                                left = 0;
                                label.style.textAlign = 'left';
                            }

                            label.style.left = left + 'px';
                            label.style.width = getAxisOption('axisLabelWidth') + 'px';
                            containerDiv.appendChild(label);
                            _this.xlabels_.push(label);
                        });
                    }

                    context.strokeStyle = g.getOptionForAxis('axisLineColor', 'x');
                    context.lineWidth = g.getOptionForAxis('axisLineWidth', 'x');
                    context.beginPath();
                    var axisY;
                    if (g.getOption('drawAxesAtZero')) {
                        var r = g.toPercentYCoord(0, 0);
                        if (r > 1 || r < 0) r = 1;
                        axisY = halfDown(area.y + r * area.h);
                    } else {
                        axisY = halfDown(area.y + area.h);
                    }
                    context.moveTo(halfUp(area.x), axisY);
                    context.lineTo(halfUp(area.x + area.w), axisY);
                    context.closePath();
                    context.stroke();
                }

                context.restore();
            };

            exports['default'] = axes;
            module.exports = exports['default'];

        }, { "../dygraph-utils": 17 }], 22: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */
            /*global Dygraph:false */

            "use strict";

            // TODO(danvk): move chart label options out of dygraphs and into the plugin.
            // TODO(danvk): only tear down & rebuild the DIVs when it's necessary.

            Object.defineProperty(exports, "__esModule", {
                value: true
            });
            var chart_labels = function chart_labels() {
                this.title_div_ = null;
                this.xlabel_div_ = null;
                this.ylabel_div_ = null;
                this.y2label_div_ = null;
            };

            chart_labels.prototype.toString = function () {
                return "ChartLabels Plugin";
            };

            chart_labels.prototype.activate = function (g) {
                return {
                    layout: this.layout,
                    // clearChart: this.clearChart,
                    didDrawChart: this.didDrawChart
                };
            };

            // QUESTION: should there be a plugin-utils.js?
            var createDivInRect = function createDivInRect(r) {
                var div = document.createElement('div');
                div.style.position = 'absolute';
                div.style.left = r.x + 'px';
                div.style.top = r.y + 'px';
                div.style.width = r.w + 'px';
                div.style.height = r.h + 'px';
                return div;
            };

            // Detach and null out any existing nodes.
            chart_labels.prototype.detachLabels_ = function () {
                var els = [this.title_div_, this.xlabel_div_, this.ylabel_div_, this.y2label_div_];
                for (var i = 0; i < els.length; i++) {
                    var el = els[i];
                    if (!el) continue;
                    if (el.parentNode) el.parentNode.removeChild(el);
                }

                this.title_div_ = null;
                this.xlabel_div_ = null;
                this.ylabel_div_ = null;
                this.y2label_div_ = null;
            };

            var createRotatedDiv = function createRotatedDiv(g, box, axis, classes, html) {
                // TODO(danvk): is this outer div actually necessary?
                var div = document.createElement("div");
                div.style.position = 'absolute';
                if (axis == 1) {
                    // NOTE: this is cheating. Should be positioned relative to the box.
                    div.style.left = '0px';
                } else {
                    div.style.left = box.x + 'px';
                }
                div.style.top = box.y + 'px';
                div.style.width = box.w + 'px';
                div.style.height = box.h + 'px';
                div.style.fontSize = g.getOption('yLabelWidth') - 2 + 'px';

                var inner_div = document.createElement("div");
                inner_div.style.position = 'absolute';
                inner_div.style.width = box.h + 'px';
                inner_div.style.height = box.w + 'px';
                inner_div.style.top = box.h / 2 - box.w / 2 + 'px';
                inner_div.style.left = box.w / 2 - box.h / 2 + 'px';
                // TODO: combine inner_div and class_div.
                inner_div.className = 'dygraph-label-rotate-' + (axis == 1 ? 'right' : 'left');

                var class_div = document.createElement("div");
                class_div.className = classes;
                class_div.innerHTML = html;

                inner_div.appendChild(class_div);
                div.appendChild(inner_div);
                return div;
            };

            chart_labels.prototype.layout = function (e) {
                this.detachLabels_();

                var g = e.dygraph;
                var div = e.chart_div;
                if (g.getOption('title')) {
                    // QUESTION: should this return an absolutely-positioned div instead?
                    var title_rect = e.reserveSpaceTop(g.getOption('titleHeight'));
                    this.title_div_ = createDivInRect(title_rect);
                    this.title_div_.style.fontSize = g.getOption('titleHeight') - 8 + 'px';

                    var class_div = document.createElement("div");
                    class_div.className = 'dygraph-label dygraph-title';
                    class_div.innerHTML = g.getOption('title');
                    this.title_div_.appendChild(class_div);
                    div.appendChild(this.title_div_);
                }

                if (g.getOption('xlabel')) {
                    var x_rect = e.reserveSpaceBottom(g.getOption('xLabelHeight'));
                    this.xlabel_div_ = createDivInRect(x_rect);
                    this.xlabel_div_.style.fontSize = g.getOption('xLabelHeight') - 2 + 'px';

                    var class_div = document.createElement("div");
                    class_div.className = 'dygraph-label dygraph-xlabel';
                    class_div.innerHTML = g.getOption('xlabel');
                    this.xlabel_div_.appendChild(class_div);
                    div.appendChild(this.xlabel_div_);
                }

                if (g.getOption('ylabel')) {
                    // It would make sense to shift the chart here to make room for the y-axis
                    // label, but the default yAxisLabelWidth is large enough that this results
                    // in overly-padded charts. The y-axis label should fit fine. If it
                    // doesn't, the yAxisLabelWidth option can be increased.
                    var y_rect = e.reserveSpaceLeft(0);

                    this.ylabel_div_ = createRotatedDiv(g, y_rect, 1, // primary (left) y-axis
                        'dygraph-label dygraph-ylabel', g.getOption('ylabel'));
                    div.appendChild(this.ylabel_div_);
                }

                if (g.getOption('y2label') && g.numAxes() == 2) {
                    // same logic applies here as for ylabel.
                    var y2_rect = e.reserveSpaceRight(0);
                    this.y2label_div_ = createRotatedDiv(g, y2_rect, 2, // secondary (right) y-axis
                        'dygraph-label dygraph-y2label', g.getOption('y2label'));
                    div.appendChild(this.y2label_div_);
                }
            };

            chart_labels.prototype.didDrawChart = function (e) {
                var g = e.dygraph;
                if (this.title_div_) {
                    this.title_div_.children[0].innerHTML = g.getOption('title');
                }
                if (this.xlabel_div_) {
                    this.xlabel_div_.children[0].innerHTML = g.getOption('xlabel');
                }
                if (this.ylabel_div_) {
                    this.ylabel_div_.children[0].children[0].innerHTML = g.getOption('ylabel');
                }
                if (this.y2label_div_) {
                    this.y2label_div_.children[0].children[0].innerHTML = g.getOption('y2label');
                }
            };

            chart_labels.prototype.clearChart = function () { };

            chart_labels.prototype.destroy = function () {
                this.detachLabels_();
            };

            exports["default"] = chart_labels;
            module.exports = exports["default"];

        }, {}], 23: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */
            /*global Dygraph:false */

            /*
            
            Current bits of jankiness:
            - Direct layout access
            - Direct area access
            
            */

            "use strict";

            /**
             * Draws the gridlines, i.e. the gray horizontal & vertical lines running the
             * length of the chart.
             *
             * @constructor
             */
            Object.defineProperty(exports, "__esModule", {
                value: true
            });
            var grid = function grid() { };

            grid.prototype.toString = function () {
                return "Gridline Plugin";
            };

            grid.prototype.activate = function (g) {
                return {
                    willDrawChart: this.willDrawChart
                };
            };

            grid.prototype.willDrawChart = function (e) {
                // Draw the new X/Y grid. Lines appear crisper when pixels are rounded to
                // half-integers. This prevents them from drawing in two rows/cols.
                var g = e.dygraph;
                var ctx = e.drawingContext;
                var layout = g.layout_;
                var area = e.dygraph.plotter_.area;

                function halfUp(x) {
                    return Math.round(x) + 0.5;
                }
                function halfDown(y) {
                    return Math.round(y) - 0.5;
                }

                var x, y, i, ticks;
                if (g.getOptionForAxis('drawGrid', 'y')) {
                    var axes = ["y", "y2"];
                    var strokeStyles = [],
                        lineWidths = [],
                        drawGrid = [],
                        stroking = [],
                        strokePattern = [];
                    for (var i = 0; i < axes.length; i++) {
                        drawGrid[i] = g.getOptionForAxis('drawGrid', axes[i]);
                        if (drawGrid[i]) {
                            strokeStyles[i] = g.getOptionForAxis('gridLineColor', axes[i]);
                            lineWidths[i] = g.getOptionForAxis('gridLineWidth', axes[i]);
                            strokePattern[i] = g.getOptionForAxis('gridLinePattern', axes[i]);
                            stroking[i] = strokePattern[i] && strokePattern[i].length >= 2;
                        }
                    }
                    ticks = layout.yticks;
                    ctx.save();
                    // draw grids for the different y axes
                    ticks.forEach(function (tick) {
                        if (!tick.has_tick) return;
                        var axis = tick.axis;
                        if (drawGrid[axis]) {
                            ctx.save();
                            if (stroking[axis]) {
                                if (ctx.setLineDash) ctx.setLineDash(strokePattern[axis]);
                            }
                            ctx.strokeStyle = strokeStyles[axis];
                            ctx.lineWidth = lineWidths[axis];

                            x = halfUp(area.x);
                            y = halfDown(area.y + tick.pos * area.h);
                            ctx.beginPath();
                            ctx.moveTo(x, y);
                            ctx.lineTo(x + area.w, y);
                            ctx.stroke();

                            ctx.restore();
                        }
                    });
                    ctx.restore();
                }

                // draw grid for x axis
                if (g.getOptionForAxis('drawGrid', 'x')) {
                    ticks = layout.xticks;
                    ctx.save();
                    var strokePattern = g.getOptionForAxis('gridLinePattern', 'x');
                    var stroking = strokePattern && strokePattern.length >= 2;
                    if (stroking) {
                        if (ctx.setLineDash) ctx.setLineDash(strokePattern);
                    }
                    ctx.strokeStyle = g.getOptionForAxis('gridLineColor', 'x');
                    ctx.lineWidth = g.getOptionForAxis('gridLineWidth', 'x');
                    ticks.forEach(function (tick) {
                        if (!tick.has_tick) return;
                        x = halfUp(area.x + tick.pos * area.w);
                        y = halfDown(area.y + area.h);
                        ctx.beginPath();
                        ctx.moveTo(x, y);
                        ctx.lineTo(x, area.y);
                        ctx.closePath();
                        ctx.stroke();
                    });
                    if (stroking) {
                        if (ctx.setLineDash) ctx.setLineDash([]);
                    }
                    ctx.restore();
                }
            };

            grid.prototype.destroy = function () { };

            exports["default"] = grid;
            module.exports = exports["default"];

        }, {}], 24: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2012 Dan Vanderkam (danvdk@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */
            /*global Dygraph:false */

            /*
            Current bits of jankiness:
            - Uses two private APIs:
                1. Dygraph.optionsViewForAxis_
                2. dygraph.plotter_.area
            - Registers for a "predraw" event, which should be renamed.
            - I call calculateEmWidthInDiv more often than needed.
            */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, "__esModule", {
                value: true
            });

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj["default"] = obj; return newObj; } }

            var _dygraphUtils = require('../dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            /**
             * Creates the legend, which appears when the user hovers over the chart.
             * The legend can be either a user-specified or generated div.
             *
             * @constructor
             */
            var Legend = function Legend() {
                this.legend_div_ = null;
                this.is_generated_div_ = false; // do we own this div, or was it user-specified?
            };

            Legend.prototype.toString = function () {
                return "Legend Plugin";
            };

            /**
             * This is called during the dygraph constructor, after options have been set
             * but before the data is available.
             *
             * Proper tasks to do here include:
             * - Reading your own options
             * - DOM manipulation
             * - Registering event listeners
             *
             * @param {Dygraph} g Graph instance.
             * @return {object.<string, function(ev)>} Mapping of event names to callbacks.
             */
            Legend.prototype.activate = function (g) {
                var div;

                var userLabelsDiv = g.getOption('labelsDiv');
                if (userLabelsDiv && null !== userLabelsDiv) {
                    if (typeof userLabelsDiv == "string" || userLabelsDiv instanceof String) {
                        div = document.getElementById(userLabelsDiv);
                    } else {
                        div = userLabelsDiv;
                    }
                } else {
                    div = document.createElement("div");
                    div.className = "dygraph-legend";
                    // TODO(danvk): come up with a cleaner way to expose this.
                    g.graphDiv.appendChild(div);
                    this.is_generated_div_ = true;
                }

                this.legend_div_ = div;
                this.one_em_width_ = 10; // just a guess, will be updated.

                return {
                    select: this.select,
                    deselect: this.deselect,
                    // TODO(danvk): rethink the name "predraw" before we commit to it in any API.
                    predraw: this.predraw,
                    didDrawChart: this.didDrawChart
                };
            };

            // Needed for dashed lines.
            var calculateEmWidthInDiv = function calculateEmWidthInDiv(div) {
                var sizeSpan = document.createElement('span');
                sizeSpan.setAttribute('style', 'margin: 0; padding: 0 0 0 1em; border: 0;');
                div.appendChild(sizeSpan);
                var oneEmWidth = sizeSpan.offsetWidth;
                div.removeChild(sizeSpan);
                return oneEmWidth;
            };

            var escapeHTML = function escapeHTML(str) {
                return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
            };

            Legend.prototype.select = function (e) {
                var xValue = e.selectedX;
                var points = e.selectedPoints;
                var row = e.selectedRow;

                var legendMode = e.dygraph.getOption('legend');
                if (legendMode === 'never') {
                    this.legend_div_.style.display = 'none';
                    return;
                }

                if (legendMode === 'follow') {
                    // create floating legend div
                    var area = e.dygraph.plotter_.area;
                    var labelsDivWidth = this.legend_div_.offsetWidth;
                    var yAxisLabelWidth = e.dygraph.getOptionForAxis('axisLabelWidth', 'y');
                    // determine floating [left, top] coordinates of the legend div
                    // within the plotter_ area
                    // offset 50 px to the right and down from the first selection point
                    // 50 px is guess based on mouse cursor size
                    var leftLegend = points[0].x * area.w + 50;
                    var topLegend = points[0].y * area.h - 50;

                    // if legend floats to end of the chart area, it flips to the other
                    // side of the selection point
                    if (leftLegend + labelsDivWidth + 1 > area.w) {
                        leftLegend = leftLegend - 2 * 50 - labelsDivWidth - (yAxisLabelWidth - area.x);
                    }

                    e.dygraph.graphDiv.appendChild(this.legend_div_);
                    this.legend_div_.style.left = yAxisLabelWidth + leftLegend + "px";
                    this.legend_div_.style.top = topLegend + "px";
                }

                var html = Legend.generateLegendHTML(e.dygraph, xValue, points, this.one_em_width_, row);
                this.legend_div_.innerHTML = html;
                this.legend_div_.style.display = '';
            };

            Legend.prototype.deselect = function (e) {
                var legendMode = e.dygraph.getOption('legend');
                if (legendMode !== 'always') {
                    this.legend_div_.style.display = "none";
                }

                // Have to do this every time, since styles might have changed.
                var oneEmWidth = calculateEmWidthInDiv(this.legend_div_);
                this.one_em_width_ = oneEmWidth;

                var html = Legend.generateLegendHTML(e.dygraph, undefined, undefined, oneEmWidth, null);
                this.legend_div_.innerHTML = html;
            };

            Legend.prototype.didDrawChart = function (e) {
                this.deselect(e);
            };

            // Right edge should be flush with the right edge of the charting area (which
            // may not be the same as the right edge of the div, if we have two y-axes.
            // TODO(danvk): is any of this really necessary? Could just set "right" in "activate".
            /**
             * Position the labels div so that:
             * - its right edge is flush with the right edge of the charting area
             * - its top edge is flush with the top edge of the charting area
             * @private
             */
            Legend.prototype.predraw = function (e) {
                // Don't touch a user-specified labelsDiv.
                if (!this.is_generated_div_) return;

                // TODO(danvk): only use real APIs for this.
                e.dygraph.graphDiv.appendChild(this.legend_div_);
                var area = e.dygraph.getArea();
                var labelsDivWidth = this.legend_div_.offsetWidth;
                this.legend_div_.style.left = area.x + area.w - labelsDivWidth - 1 + "px";
                this.legend_div_.style.top = area.y + "px";
            };

            /**
             * Called when dygraph.destroy() is called.
             * You should null out any references and detach any DOM elements.
             */
            Legend.prototype.destroy = function () {
                this.legend_div_ = null;
            };

            /**
             * Generates HTML for the legend which is displayed when hovering over the
             * chart. If no selected points are specified, a default legend is returned
             * (this may just be the empty string).
             * @param {number} x The x-value of the selected points.
             * @param {Object} sel_points List of selected points for the given
             *   x-value. Should have properties like 'name', 'yval' and 'canvasy'.
             * @param {number} oneEmWidth The pixel width for 1em in the legend. Only
             *   relevant when displaying a legend with no selection (i.e. {legend:
             *   'always'}) and with dashed lines.
             * @param {number} row The selected row index.
             * @private
             */
            Legend.generateLegendHTML = function (g, x, sel_points, oneEmWidth, row) {
                // Data about the selection to pass to legendFormatter
                var data = {
                    dygraph: g,
                    x: x,
                    series: []
                };

                var labelToSeries = {};
                var labels = g.getLabels();
                if (labels) {
                    for (var i = 1; i < labels.length; i++) {
                        var series = g.getPropertiesForSeries(labels[i]);
                        var strokePattern = g.getOption('strokePattern', labels[i]);
                        var seriesData = {
                            dashHTML: generateLegendDashHTML(strokePattern, series.color, oneEmWidth),
                            label: labels[i],
                            labelHTML: escapeHTML(labels[i]),
                            isVisible: series.visible,
                            color: series.color
                        };

                        data.series.push(seriesData);
                        labelToSeries[labels[i]] = seriesData;
                    }
                }

                if (typeof x !== 'undefined') {
                    var xOptView = g.optionsViewForAxis_('x');
                    var xvf = xOptView('valueFormatter');
                    data.xHTML = xvf.call(g, x, xOptView, labels[0], g, row, 0);

                    var yOptViews = [];
                    var num_axes = g.numAxes();
                    for (var i = 0; i < num_axes; i++) {
                        // TODO(danvk): remove this use of a private API
                        yOptViews[i] = g.optionsViewForAxis_('y' + (i ? 1 + i : ''));
                    }

                    var showZeros = g.getOption('labelsShowZeroValues');
                    var highlightSeries = g.getHighlightSeries();
                    for (i = 0; i < sel_points.length; i++) {
                        var pt = sel_points[i];
                        var seriesData = labelToSeries[pt.name];
                        seriesData.y = pt.yval;

                        if (pt.yval === 0 && !showZeros || isNaN(pt.canvasy)) {
                            seriesData.isVisible = false;
                            continue;
                        }

                        var series = g.getPropertiesForSeries(pt.name);
                        var yOptView = yOptViews[series.axis - 1];
                        var fmtFunc = yOptView('valueFormatter');
                        var yHTML = fmtFunc.call(g, pt.yval, yOptView, pt.name, g, row, labels.indexOf(pt.name));

                        utils.update(seriesData, { yHTML: yHTML });

                        if (pt.name == highlightSeries) {
                            seriesData.isHighlighted = true;
                        }
                    }
                }

                var formatter = g.getOption('legendFormatter') || Legend.defaultFormatter;
                return formatter.call(g, data);
            };

            Legend.defaultFormatter = function (data) {
                var g = data.dygraph;

                // TODO(danvk): deprecate this option in place of {legend: 'never'}
                // XXX should this logic be in the formatter?
                if (g.getOption('showLabelsOnHighlight') !== true) return '';

                var sepLines = g.getOption('labelsSeparateLines');
                var html;

                if (typeof data.x === 'undefined') {
                    // TODO: this check is duplicated in generateLegendHTML. Put it in one place.
                    if (g.getOption('legend') != 'always') {
                        return '';
                    }

                    html = '';
                    for (var i = 0; i < data.series.length; i++) {
                        var series = data.series[i];
                        if (!series.isVisible) continue;

                        if (html !== '') html += sepLines ? '<br/>' : ' ';
                        html += "<span style='font-weight: bold; color: " + series.color + ";'>" + series.dashHTML + " " + series.labelHTML + "</span>";
                    }
                    return html;
                }

                html = data.xHTML + ':';
                for (var i = 0; i < data.series.length; i++) {
                    var series = data.series[i];
                    if (!series.isVisible) continue;
                    if (sepLines) html += '<br>';
                    var cls = series.isHighlighted ? ' class="highlight"' : '';
                    html += "<span" + cls + "> <b><span style='color: " + series.color + ";'>" + series.labelHTML + "</span></b>:&#160;" + series.yHTML + "</span>";
                }
                return html;
            };

            /**
             * Generates html for the "dash" displayed on the legend when using "legend: always".
             * In particular, this works for dashed lines with any stroke pattern. It will
             * try to scale the pattern to fit in 1em width. Or if small enough repeat the
             * pattern for 1em width.
             *
             * @param strokePattern The pattern
             * @param color The color of the series.
             * @param oneEmWidth The width in pixels of 1em in the legend.
             * @private
             */
            // TODO(danvk): cache the results of this
            function generateLegendDashHTML(strokePattern, color, oneEmWidth) {
                // Easy, common case: a solid line
                if (!strokePattern || strokePattern.length <= 1) {
                    return "<div class=\"dygraph-legend-line\" style=\"border-bottom-color: " + color + ";\"></div>";
                }

                var i, j, paddingLeft, marginRight;
                var strokePixelLength = 0,
                    segmentLoop = 0;
                var normalizedPattern = [];
                var loop;

                // Compute the length of the pixels including the first segment twice,
                // since we repeat it.
                for (i = 0; i <= strokePattern.length; i++) {
                    strokePixelLength += strokePattern[i % strokePattern.length];
                }

                // See if we can loop the pattern by itself at least twice.
                loop = Math.floor(oneEmWidth / (strokePixelLength - strokePattern[0]));
                if (loop > 1) {
                    // This pattern fits at least two times, no scaling just convert to em;
                    for (i = 0; i < strokePattern.length; i++) {
                        normalizedPattern[i] = strokePattern[i] / oneEmWidth;
                    }
                    // Since we are repeating the pattern, we don't worry about repeating the
                    // first segment in one draw.
                    segmentLoop = normalizedPattern.length;
                } else {
                    // If the pattern doesn't fit in the legend we scale it to fit.
                    loop = 1;
                    for (i = 0; i < strokePattern.length; i++) {
                        normalizedPattern[i] = strokePattern[i] / strokePixelLength;
                    }
                    // For the scaled patterns we do redraw the first segment.
                    segmentLoop = normalizedPattern.length + 1;
                }

                // Now make the pattern.
                var dash = "";
                for (j = 0; j < loop; j++) {
                    for (i = 0; i < segmentLoop; i += 2) {
                        // The padding is the drawn segment.
                        paddingLeft = normalizedPattern[i % normalizedPattern.length];
                        if (i < strokePattern.length) {
                            // The margin is the space segment.
                            marginRight = normalizedPattern[(i + 1) % normalizedPattern.length];
                        } else {
                            // The repeated first segment has no right margin.
                            marginRight = 0;
                        }
                        dash += "<div class=\"dygraph-legend-dash\" style=\"margin-right: " + marginRight + "em; padding-left: " + paddingLeft + "em;\"></div>";
                    }
                }
                return dash;
            };

            exports["default"] = Legend;
            module.exports = exports["default"];

        }, { "../dygraph-utils": 17 }], 25: [function (require, module, exports) {
            /**
             * @license
             * Copyright 2011 Paul Felix (paul.eric.felix@gmail.com)
             * MIT-licensed (http://opensource.org/licenses/MIT)
             */
            /*global Dygraph:false,TouchEvent:false */

            /**
             * @fileoverview This file contains the RangeSelector plugin used to provide
             * a timeline range selector widget for dygraphs.
             */

            /*global Dygraph:false */
            "use strict";

            Object.defineProperty(exports, '__esModule', {
                value: true
            });

            function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

            function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }

            var _dygraphUtils = require('../dygraph-utils');

            var utils = _interopRequireWildcard(_dygraphUtils);

            var _dygraphInteractionModel = require('../dygraph-interaction-model');

            var _dygraphInteractionModel2 = _interopRequireDefault(_dygraphInteractionModel);

            var _iframeTarp = require('../iframe-tarp');

            var _iframeTarp2 = _interopRequireDefault(_iframeTarp);

            var rangeSelector = function rangeSelector() {
                this.hasTouchInterface_ = typeof TouchEvent != 'undefined';
                this.isMobileDevice_ = /mobile|android/gi.test(navigator.appVersion);
                this.interfaceCreated_ = false;
            };

            rangeSelector.prototype.toString = function () {
                return "RangeSelector Plugin";
            };

            rangeSelector.prototype.activate = function (dygraph) {
                this.dygraph_ = dygraph;
                if (this.getOption_('showRangeSelector')) {
                    this.createInterface_();
                }
                return {
                    layout: this.reserveSpace_,
                    predraw: this.renderStaticLayer_,
                    didDrawChart: this.renderInteractiveLayer_
                };
            };

            rangeSelector.prototype.destroy = function () {
                this.bgcanvas_ = null;
                this.fgcanvas_ = null;
                this.leftZoomHandle_ = null;
                this.rightZoomHandle_ = null;
            };

            //------------------------------------------------------------------
            // Private methods
            //------------------------------------------------------------------

            rangeSelector.prototype.getOption_ = function (name, opt_series) {
                return this.dygraph_.getOption(name, opt_series);
            };

            rangeSelector.prototype.setDefaultOption_ = function (name, value) {
                this.dygraph_.attrs_[name] = value;
            };

            /**
             * @private
             * Creates the range selector elements and adds them to the graph.
             */
            rangeSelector.prototype.createInterface_ = function () {
                this.createCanvases_();
                this.createZoomHandles_();
                this.initInteraction_();

                // Range selector and animatedZooms have a bad interaction. See issue 359.
                if (this.getOption_('animatedZooms')) {
                    console.warn('Animated zooms and range selector are not compatible; disabling animatedZooms.');
                    this.dygraph_.updateOptions({ animatedZooms: false }, true);
                }

                this.interfaceCreated_ = true;
                this.addToGraph_();
            };

            /**
             * @private
             * Adds the range selector to the graph.
             */
            rangeSelector.prototype.addToGraph_ = function () {
                var graphDiv = this.graphDiv_ = this.dygraph_.graphDiv;
                graphDiv.appendChild(this.bgcanvas_);
                graphDiv.appendChild(this.fgcanvas_);
                graphDiv.appendChild(this.leftZoomHandle_);
                graphDiv.appendChild(this.rightZoomHandle_);
            };

            /**
             * @private
             * Removes the range selector from the graph.
             */
            rangeSelector.prototype.removeFromGraph_ = function () {
                var graphDiv = this.graphDiv_;
                graphDiv.removeChild(this.bgcanvas_);
                graphDiv.removeChild(this.fgcanvas_);
                graphDiv.removeChild(this.leftZoomHandle_);
                graphDiv.removeChild(this.rightZoomHandle_);
                this.graphDiv_ = null;
            };

            /**
             * @private
             * Called by Layout to allow range selector to reserve its space.
             */
            rangeSelector.prototype.reserveSpace_ = function (e) {
                if (this.getOption_('showRangeSelector')) {
                    e.reserveSpaceBottom(this.getOption_('rangeSelectorHeight') + 4);
                }
            };

            /**
             * @private
             * Renders the static portion of the range selector at the predraw stage.
             */
            rangeSelector.prototype.renderStaticLayer_ = function () {
                if (!this.updateVisibility_()) {
                    return;
                }
                this.resize_();
                this.drawStaticLayer_();
            };

            /**
             * @private
             * Renders the interactive portion of the range selector after the chart has been drawn.
             */
            rangeSelector.prototype.renderInteractiveLayer_ = function () {
                if (!this.updateVisibility_() || this.isChangingRange_) {
                    return;
                }
                this.placeZoomHandles_();
                this.drawInteractiveLayer_();
            };

            /**
             * @private
             * Check to see if the range selector is enabled/disabled and update visibility accordingly.
             */
            rangeSelector.prototype.updateVisibility_ = function () {
                var enabled = this.getOption_('showRangeSelector');
                if (enabled) {
                    if (!this.interfaceCreated_) {
                        this.createInterface_();
                    } else if (!this.graphDiv_ || !this.graphDiv_.parentNode) {
                        this.addToGraph_();
                    }
                } else if (this.graphDiv_) {
                    this.removeFromGraph_();
                    var dygraph = this.dygraph_;
                    setTimeout(function () {
                        dygraph.width_ = 0; dygraph.resize();
                    }, 1);
                }
                return enabled;
            };

            /**
             * @private
             * Resizes the range selector.
             */
            rangeSelector.prototype.resize_ = function () {
                function setElementRect(canvas, context, rect, pixelRatioOption) {
                    var canvasScale = pixelRatioOption || utils.getContextPixelRatio(context);

                    canvas.style.top = rect.y + 'px';
                    canvas.style.left = rect.x + 'px';
                    canvas.width = rect.w * canvasScale;
                    canvas.height = rect.h * canvasScale;
                    canvas.style.width = rect.w + 'px';
                    canvas.style.height = rect.h + 'px';

                    if (canvasScale != 1) {
                        context.scale(canvasScale, canvasScale);
                    }
                }

                var plotArea = this.dygraph_.layout_.getPlotArea();

                var xAxisLabelHeight = 0;
                if (this.dygraph_.getOptionForAxis('drawAxis', 'x')) {
                    xAxisLabelHeight = this.getOption_('xAxisHeight') || this.getOption_('axisLabelFontSize') + 2 * this.getOption_('axisTickSize');
                }
                this.canvasRect_ = {
                    x: plotArea.x,
                    y: plotArea.y + plotArea.h + xAxisLabelHeight + 4,
                    w: plotArea.w,
                    h: this.getOption_('rangeSelectorHeight')
                };

                var pixelRatioOption = this.dygraph_.getNumericOption('pixelRatio');
                setElementRect(this.bgcanvas_, this.bgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
                setElementRect(this.fgcanvas_, this.fgcanvas_ctx_, this.canvasRect_, pixelRatioOption);
            };

            /**
             * @private
             * Creates the background and foreground canvases.
             */
            rangeSelector.prototype.createCanvases_ = function () {
                this.bgcanvas_ = utils.createCanvas();
                this.bgcanvas_.className = 'dygraph-rangesel-bgcanvas';
                this.bgcanvas_.style.position = 'absolute';
                this.bgcanvas_.style.zIndex = 9;
                this.bgcanvas_ctx_ = utils.getContext(this.bgcanvas_);

                this.fgcanvas_ = utils.createCanvas();
                this.fgcanvas_.className = 'dygraph-rangesel-fgcanvas';
                this.fgcanvas_.style.position = 'absolute';
                this.fgcanvas_.style.zIndex = 9;
                this.fgcanvas_.style.cursor = 'default';
                this.fgcanvas_ctx_ = utils.getContext(this.fgcanvas_);
            };

            /**
             * @private
             * Creates the zoom handle elements.
             */
            rangeSelector.prototype.createZoomHandles_ = function () {
                var img = new Image();
                img.className = 'dygraph-rangesel-zoomhandle';
                img.style.position = 'absolute';
                img.style.zIndex = 10;
                img.style.visibility = 'hidden'; // Initially hidden so they don't show up in the wrong place.
                img.style.cursor = 'col-resize';
                // TODO: change image to more options
                img.width = 9;
                img.height = 16;
                img.src = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAAAkAAAAQCAYAAADESFVDAAAAAXNSR0IArs4c6QAAAAZiS0dEANAA' + 'zwDP4Z7KegAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9sHGw0cMqdt1UwAAAAZdEVYdENv' + 'bW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAaElEQVQoz+3SsRFAQBCF4Z9WJM8KCDVwownl' + '6YXsTmCUsyKGkZzcl7zkz3YLkypgAnreFmDEpHkIwVOMfpdi9CEEN2nGpFdwD03yEqDtOgCaun7s' + 'qSTDH32I1pQA2Pb9sZecAxc5r3IAb21d6878xsAAAAAASUVORK5CYII=';

                if (this.isMobileDevice_) {
                    img.width *= 2;
                    img.height *= 2;
                }

                this.leftZoomHandle_ = img;
                this.rightZoomHandle_ = img.cloneNode(false);
            };

            /**
             * @private
             * Sets up the interaction for the range selector.
             */
            rangeSelector.prototype.initInteraction_ = function () {
                var self = this;
                var topElem = document;
                var clientXLast = 0;
                var handle = null;
                var isZooming = false;
                var isPanning = false;
                var dynamic = !this.isMobileDevice_;

                // We cover iframes during mouse interactions. See comments in
                // dygraph-utils.js for more info on why this is a good idea.
                var tarp = new _iframeTarp2['default']();

                // functions, defined below.  Defining them this way (rather than with
                // "function foo() {...}" makes JSHint happy.
                var toXDataWindow, onZoomStart, onZoom, onZoomEnd, doZoom, isMouseInPanZone, onPanStart, onPan, onPanEnd, doPan, onCanvasHover;

                // Touch event functions
                var onZoomHandleTouchEvent, onCanvasTouchEvent, addTouchEvents;

                toXDataWindow = function (zoomHandleStatus) {
                    var xDataLimits = self.dygraph_.xAxisExtremes();
                    var fact = (xDataLimits[1] - xDataLimits[0]) / self.canvasRect_.w;
                    var xDataMin = xDataLimits[0] + (zoomHandleStatus.leftHandlePos - self.canvasRect_.x) * fact;
                    var xDataMax = xDataLimits[0] + (zoomHandleStatus.rightHandlePos - self.canvasRect_.x) * fact;
                    return [xDataMin, xDataMax];
                };

                onZoomStart = function (e) {
                    utils.cancelEvent(e);
                    isZooming = true;
                    clientXLast = e.clientX;
                    handle = e.target ? e.target : e.srcElement;
                    if (e.type === 'mousedown' || e.type === 'dragstart') {
                        // These events are removed manually.
                        utils.addEvent(topElem, 'mousemove', onZoom);
                        utils.addEvent(topElem, 'mouseup', onZoomEnd);
                    }
                    self.fgcanvas_.style.cursor = 'col-resize';
                    tarp.cover();
                    return true;
                };

                onZoom = function (e) {
                    if (!isZooming) {
                        return false;
                    }
                    utils.cancelEvent(e);

                    var delX = e.clientX - clientXLast;
                    if (Math.abs(delX) < 4) {
                        return true;
                    }
                    clientXLast = e.clientX;

                    // Move handle.
                    var zoomHandleStatus = self.getZoomHandleStatus_();
                    var newPos;
                    if (handle == self.leftZoomHandle_) {
                        newPos = zoomHandleStatus.leftHandlePos + delX;
                        newPos = Math.min(newPos, zoomHandleStatus.rightHandlePos - handle.width - 3);
                        newPos = Math.max(newPos, self.canvasRect_.x);
                    } else {
                        newPos = zoomHandleStatus.rightHandlePos + delX;
                        newPos = Math.min(newPos, self.canvasRect_.x + self.canvasRect_.w);
                        newPos = Math.max(newPos, zoomHandleStatus.leftHandlePos + handle.width + 3);
                    }
                    var halfHandleWidth = handle.width / 2;
                    handle.style.left = newPos - halfHandleWidth + 'px';
                    self.drawInteractiveLayer_();

                    // Zoom on the fly.
                    if (dynamic) {
                        doZoom();
                    }
                    return true;
                };

                onZoomEnd = function (e) {
                    if (!isZooming) {
                        return false;
                    }
                    isZooming = false;
                    tarp.uncover();
                    utils.removeEvent(topElem, 'mousemove', onZoom);
                    utils.removeEvent(topElem, 'mouseup', onZoomEnd);
                    self.fgcanvas_.style.cursor = 'default';

                    // If on a slower device, zoom now.
                    if (!dynamic) {
                        doZoom();
                    }
                    return true;
                };

                doZoom = function () {
                    try {
                        var zoomHandleStatus = self.getZoomHandleStatus_();
                        self.isChangingRange_ = true;
                        if (!zoomHandleStatus.isZoomed) {
                            self.dygraph_.resetZoom();
                        } else {
                            var xDataWindow = toXDataWindow(zoomHandleStatus);
                            self.dygraph_.doZoomXDates_(xDataWindow[0], xDataWindow[1]);
                        }
                    } finally {
                        self.isChangingRange_ = false;
                    }
                };

                isMouseInPanZone = function (e) {
                    var rect = self.leftZoomHandle_.getBoundingClientRect();
                    var leftHandleClientX = rect.left + rect.width / 2;
                    rect = self.rightZoomHandle_.getBoundingClientRect();
                    var rightHandleClientX = rect.left + rect.width / 2;
                    return e.clientX > leftHandleClientX && e.clientX < rightHandleClientX;
                };

                onPanStart = function (e) {
                    if (!isPanning && isMouseInPanZone(e) && self.getZoomHandleStatus_().isZoomed) {
                        utils.cancelEvent(e);
                        isPanning = true;
                        clientXLast = e.clientX;
                        if (e.type === 'mousedown') {
                            // These events are removed manually.
                            utils.addEvent(topElem, 'mousemove', onPan);
                            utils.addEvent(topElem, 'mouseup', onPanEnd);
                        }
                        return true;
                    }
                    return false;
                };

                onPan = function (e) {
                    if (!isPanning) {
                        return false;
                    }
                    utils.cancelEvent(e);

                    var delX = e.clientX - clientXLast;
                    if (Math.abs(delX) < 4) {
                        return true;
                    }
                    clientXLast = e.clientX;

                    // Move range view
                    var zoomHandleStatus = self.getZoomHandleStatus_();
                    var leftHandlePos = zoomHandleStatus.leftHandlePos;
                    var rightHandlePos = zoomHandleStatus.rightHandlePos;
                    var rangeSize = rightHandlePos - leftHandlePos;
                    if (leftHandlePos + delX <= self.canvasRect_.x) {
                        leftHandlePos = self.canvasRect_.x;
                        rightHandlePos = leftHandlePos + rangeSize;
                    } else if (rightHandlePos + delX >= self.canvasRect_.x + self.canvasRect_.w) {
                        rightHandlePos = self.canvasRect_.x + self.canvasRect_.w;
                        leftHandlePos = rightHandlePos - rangeSize;
                    } else {
                        leftHandlePos += delX;
                        rightHandlePos += delX;
                    }
                    var halfHandleWidth = self.leftZoomHandle_.width / 2;
                    self.leftZoomHandle_.style.left = leftHandlePos - halfHandleWidth + 'px';
                    self.rightZoomHandle_.style.left = rightHandlePos - halfHandleWidth + 'px';
                    self.drawInteractiveLayer_();

                    // Do pan on the fly.
                    if (dynamic) {
                        doPan();
                    }
                    return true;
                };

                onPanEnd = function (e) {
                    if (!isPanning) {
                        return false;
                    }
                    isPanning = false;
                    utils.removeEvent(topElem, 'mousemove', onPan);
                    utils.removeEvent(topElem, 'mouseup', onPanEnd);
                    // If on a slower device, do pan now.
                    if (!dynamic) {
                        doPan();
                    }
                    return true;
                };

                doPan = function () {
                    try {
                        self.isChangingRange_ = true;
                        self.dygraph_.dateWindow_ = toXDataWindow(self.getZoomHandleStatus_());
                        self.dygraph_.drawGraph_(false);
                    } finally {
                        self.isChangingRange_ = false;
                    }
                };

                onCanvasHover = function (e) {
                    if (isZooming || isPanning) {
                        return;
                    }
                    var cursor = isMouseInPanZone(e) ? 'move' : 'default';
                    if (cursor != self.fgcanvas_.style.cursor) {
                        self.fgcanvas_.style.cursor = cursor;
                    }
                };

                onZoomHandleTouchEvent = function (e) {
                    if (e.type == 'touchstart' && e.targetTouches.length == 1) {
                        if (onZoomStart(e.targetTouches[0])) {
                            utils.cancelEvent(e);
                        }
                    } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
                        if (onZoom(e.targetTouches[0])) {
                            utils.cancelEvent(e);
                        }
                    } else {
                        onZoomEnd(e);
                    }
                };

                onCanvasTouchEvent = function (e) {
                    if (e.type == 'touchstart' && e.targetTouches.length == 1) {
                        if (onPanStart(e.targetTouches[0])) {
                            utils.cancelEvent(e);
                        }
                    } else if (e.type == 'touchmove' && e.targetTouches.length == 1) {
                        if (onPan(e.targetTouches[0])) {
                            utils.cancelEvent(e);
                        }
                    } else {
                        onPanEnd(e);
                    }
                };

                addTouchEvents = function (elem, fn) {
                    var types = ['touchstart', 'touchend', 'touchmove', 'touchcancel'];
                    for (var i = 0; i < types.length; i++) {
                        self.dygraph_.addAndTrackEvent(elem, types[i], fn);
                    }
                };

                this.setDefaultOption_('interactionModel', _dygraphInteractionModel2['default'].dragIsPanInteractionModel);
                this.setDefaultOption_('panEdgeFraction', 0.0001);

                var dragStartEvent = window.opera ? 'mousedown' : 'dragstart';
                this.dygraph_.addAndTrackEvent(this.leftZoomHandle_, dragStartEvent, onZoomStart);
                this.dygraph_.addAndTrackEvent(this.rightZoomHandle_, dragStartEvent, onZoomStart);

                this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousedown', onPanStart);
                this.dygraph_.addAndTrackEvent(this.fgcanvas_, 'mousemove', onCanvasHover);

                // Touch events
                if (this.hasTouchInterface_) {
                    addTouchEvents(this.leftZoomHandle_, onZoomHandleTouchEvent);
                    addTouchEvents(this.rightZoomHandle_, onZoomHandleTouchEvent);
                    addTouchEvents(this.fgcanvas_, onCanvasTouchEvent);
                }
            };

            /**
             * @private
             * Draws the static layer in the background canvas.
             */
            rangeSelector.prototype.drawStaticLayer_ = function () {
                var ctx = this.bgcanvas_ctx_;
                ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h);
                try {
                    this.drawMiniPlot_();
                } catch (ex) {
                    console.warn(ex);
                }

                var margin = 0.5;
                this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorBackgroundLineWidth');
                ctx.strokeStyle = this.getOption_('rangeSelectorBackgroundStrokeColor');
                ctx.beginPath();
                ctx.moveTo(margin, margin);
                ctx.lineTo(margin, this.canvasRect_.h - margin);
                ctx.lineTo(this.canvasRect_.w - margin, this.canvasRect_.h - margin);
                ctx.lineTo(this.canvasRect_.w - margin, margin);
                ctx.stroke();
            };

            /**
             * @private
             * Draws the mini plot in the background canvas.
             */
            rangeSelector.prototype.drawMiniPlot_ = function () {
                var fillStyle = this.getOption_('rangeSelectorPlotFillColor');
                var fillGradientStyle = this.getOption_('rangeSelectorPlotFillGradientColor');
                var strokeStyle = this.getOption_('rangeSelectorPlotStrokeColor');
                if (!fillStyle && !strokeStyle) {
                    return;
                }

                var stepPlot = this.getOption_('stepPlot');

                var combinedSeriesData = this.computeCombinedSeriesAndLimits_();
                var yRange = combinedSeriesData.yMax - combinedSeriesData.yMin;

                // Draw the mini plot.
                var ctx = this.bgcanvas_ctx_;
                var margin = 0.5;

                var xExtremes = this.dygraph_.xAxisExtremes();
                var xRange = Math.max(xExtremes[1] - xExtremes[0], 1.e-30);
                var xFact = (this.canvasRect_.w - margin) / xRange;
                var yFact = (this.canvasRect_.h - margin) / yRange;
                var canvasWidth = this.canvasRect_.w - margin;
                var canvasHeight = this.canvasRect_.h - margin;

                var prevX = null,
                    prevY = null;

                ctx.beginPath();
                ctx.moveTo(margin, canvasHeight);
                for (var i = 0; i < combinedSeriesData.data.length; i++) {
                    var dataPoint = combinedSeriesData.data[i];
                    var x = dataPoint[0] !== null ? (dataPoint[0] - xExtremes[0]) * xFact : NaN;
                    var y = dataPoint[1] !== null ? canvasHeight - (dataPoint[1] - combinedSeriesData.yMin) * yFact : NaN;

                    // Skip points that don't change the x-value. Overly fine-grained points
                    // can cause major slowdowns with the ctx.fill() call below.
                    if (!stepPlot && prevX !== null && Math.round(x) == Math.round(prevX)) {
                        continue;
                    }

                    if (isFinite(x) && isFinite(y)) {
                        if (prevX === null) {
                            ctx.lineTo(x, canvasHeight);
                        } else if (stepPlot) {
                            ctx.lineTo(x, prevY);
                        }
                        ctx.lineTo(x, y);
                        prevX = x;
                        prevY = y;
                    } else {
                        if (prevX !== null) {
                            if (stepPlot) {
                                ctx.lineTo(x, prevY);
                                ctx.lineTo(x, canvasHeight);
                            } else {
                                ctx.lineTo(prevX, canvasHeight);
                            }
                        }
                        prevX = prevY = null;
                    }
                }
                ctx.lineTo(canvasWidth, canvasHeight);
                ctx.closePath();

                if (fillStyle) {
                    var lingrad = this.bgcanvas_ctx_.createLinearGradient(0, 0, 0, canvasHeight);
                    if (fillGradientStyle) {
                        lingrad.addColorStop(0, fillGradientStyle);
                    }
                    lingrad.addColorStop(1, fillStyle);
                    this.bgcanvas_ctx_.fillStyle = lingrad;
                    ctx.fill();
                }

                if (strokeStyle) {
                    this.bgcanvas_ctx_.strokeStyle = strokeStyle;
                    this.bgcanvas_ctx_.lineWidth = this.getOption_('rangeSelectorPlotLineWidth');
                    ctx.stroke();
                }
            };

            /**
             * @private
             * Computes and returns the combined series data along with min/max for the mini plot.
             * The combined series consists of averaged values for all series.
             * When series have error bars, the error bars are ignored.
             * @return {Object} An object containing combined series array, ymin, ymax.
             */
            rangeSelector.prototype.computeCombinedSeriesAndLimits_ = function () {
                var g = this.dygraph_;
                var logscale = this.getOption_('logscale');
                var i;

                // Select series to combine. By default, all series are combined.
                var numColumns = g.numColumns();
                var labels = g.getLabels();
                var includeSeries = new Array(numColumns);
                var anySet = false;
                var visibility = g.visibility();
                var inclusion = [];

                for (i = 1; i < numColumns; i++) {
                    var include = this.getOption_('showInRangeSelector', labels[i]);
                    inclusion.push(include);
                    if (include !== null) anySet = true; // it's set explicitly for this series
                }

                if (anySet) {
                    for (i = 1; i < numColumns; i++) {
                        includeSeries[i] = inclusion[i - 1];
                    }
                } else {
                    for (i = 1; i < numColumns; i++) {
                        includeSeries[i] = visibility[i - 1];
                    }
                }

                // Create a combined series (average of selected series values).
                // TODO(danvk): short-circuit if there's only one series.
                var rolledSeries = [];
                var dataHandler = g.dataHandler_;
                var options = g.attributes_;
                for (i = 1; i < g.numColumns(); i++) {
                    if (!includeSeries[i]) continue;
                    var series = dataHandler.extractSeries(g.rawData_, i, options);
                    if (g.rollPeriod() > 1) {
                        series = dataHandler.rollingAverage(series, g.rollPeriod(), options);
                    }

                    rolledSeries.push(series);
                }

                var combinedSeries = [];
                for (i = 0; i < rolledSeries[0].length; i++) {
                    var sum = 0;
                    var count = 0;
                    for (var j = 0; j < rolledSeries.length; j++) {
                        var y = rolledSeries[j][i][1];
                        if (y === null || isNaN(y)) continue;
                        count++;
                        sum += y;
                    }
                    combinedSeries.push([rolledSeries[0][i][0], sum / count]);
                }

                // Compute the y range.
                var yMin = Number.MAX_VALUE;
                var yMax = -Number.MAX_VALUE;
                for (i = 0; i < combinedSeries.length; i++) {
                    var yVal = combinedSeries[i][1];
                    if (yVal !== null && isFinite(yVal) && (!logscale || yVal > 0)) {
                        yMin = Math.min(yMin, yVal);
                        yMax = Math.max(yMax, yVal);
                    }
                }

                // Convert Y data to log scale if needed.
                // Also, expand the Y range to compress the mini plot a little.
                var extraPercent = 0.25;
                if (logscale) {
                    yMax = utils.log10(yMax);
                    yMax += yMax * extraPercent;
                    yMin = utils.log10(yMin);
                    for (i = 0; i < combinedSeries.length; i++) {
                        combinedSeries[i][1] = utils.log10(combinedSeries[i][1]);
                    }
                } else {
                    var yExtra;
                    var yRange = yMax - yMin;
                    if (yRange <= Number.MIN_VALUE) {
                        yExtra = yMax * extraPercent;
                    } else {
                        yExtra = yRange * extraPercent;
                    }
                    yMax += yExtra;
                    yMin -= yExtra;
                }

                return { data: combinedSeries, yMin: yMin, yMax: yMax };
            };

            /**
             * @private
             * Places the zoom handles in the proper position based on the current X data window.
             */
            rangeSelector.prototype.placeZoomHandles_ = function () {
                var xExtremes = this.dygraph_.xAxisExtremes();
                var xWindowLimits = this.dygraph_.xAxisRange();
                var xRange = xExtremes[1] - xExtremes[0];
                var leftPercent = Math.max(0, (xWindowLimits[0] - xExtremes[0]) / xRange);
                var rightPercent = Math.max(0, (xExtremes[1] - xWindowLimits[1]) / xRange);
                var leftCoord = this.canvasRect_.x + this.canvasRect_.w * leftPercent;
                var rightCoord = this.canvasRect_.x + this.canvasRect_.w * (1 - rightPercent);
                var handleTop = Math.max(this.canvasRect_.y, this.canvasRect_.y + (this.canvasRect_.h - this.leftZoomHandle_.height) / 2);
                var halfHandleWidth = this.leftZoomHandle_.width / 2;
                this.leftZoomHandle_.style.left = leftCoord - halfHandleWidth + 'px';
                this.leftZoomHandle_.style.top = handleTop + 'px';
                this.rightZoomHandle_.style.left = rightCoord - halfHandleWidth + 'px';
                this.rightZoomHandle_.style.top = this.leftZoomHandle_.style.top;

                this.leftZoomHandle_.style.visibility = 'visible';
                this.rightZoomHandle_.style.visibility = 'visible';
            };

            /**
             * @private
             * Draws the interactive layer in the foreground canvas.
             */
            rangeSelector.prototype.drawInteractiveLayer_ = function () {
                var ctx = this.fgcanvas_ctx_;
                ctx.clearRect(0, 0, this.canvasRect_.w, this.canvasRect_.h);
                var margin = 1;
                var width = this.canvasRect_.w - margin;
                var height = this.canvasRect_.h - margin;
                var zoomHandleStatus = this.getZoomHandleStatus_();

                ctx.strokeStyle = this.getOption_('rangeSelectorForegroundStrokeColor');
                ctx.lineWidth = this.getOption_('rangeSelectorForegroundLineWidth');
                if (!zoomHandleStatus.isZoomed) {
                    ctx.beginPath();
                    ctx.moveTo(margin, margin);
                    ctx.lineTo(margin, height);
                    ctx.lineTo(width, height);
                    ctx.lineTo(width, margin);
                    ctx.stroke();
                } else {
                    var leftHandleCanvasPos = Math.max(margin, zoomHandleStatus.leftHandlePos - this.canvasRect_.x);
                    var rightHandleCanvasPos = Math.min(width, zoomHandleStatus.rightHandlePos - this.canvasRect_.x);

                    ctx.fillStyle = 'rgba(240, 240, 240, ' + this.getOption_('rangeSelectorAlpha').toString() + ')';
                    ctx.fillRect(0, 0, leftHandleCanvasPos, this.canvasRect_.h);
                    ctx.fillRect(rightHandleCanvasPos, 0, this.canvasRect_.w - rightHandleCanvasPos, this.canvasRect_.h);

                    ctx.beginPath();
                    ctx.moveTo(margin, margin);
                    ctx.lineTo(leftHandleCanvasPos, margin);
                    ctx.lineTo(leftHandleCanvasPos, height);
                    ctx.lineTo(rightHandleCanvasPos, height);
                    ctx.lineTo(rightHandleCanvasPos, margin);
                    ctx.lineTo(width, margin);
                    ctx.stroke();
                }
            };

            /**
             * @private
             * Returns the current zoom handle position information.
             * @return {Object} The zoom handle status.
             */
            rangeSelector.prototype.getZoomHandleStatus_ = function () {
                var halfHandleWidth = this.leftZoomHandle_.width / 2;
                var leftHandlePos = parseFloat(this.leftZoomHandle_.style.left) + halfHandleWidth;
                var rightHandlePos = parseFloat(this.rightZoomHandle_.style.left) + halfHandleWidth;
                return {
                    leftHandlePos: leftHandlePos,
                    rightHandlePos: rightHandlePos,
                    isZoomed: leftHandlePos - 1 > this.canvasRect_.x || rightHandlePos + 1 < this.canvasRect_.x + this.canvasRect_.w
                };
            };

            exports['default'] = rangeSelector;
            module.exports = exports['default'];

        }, { "../dygraph-interaction-model": 12, "../dygraph-utils": 17, "../iframe-tarp": 19 }]
    }, {}, [18])(18)
});
//# sourceMappingURL=dygraph.js.map;
/**
 * @license
 * Copyright 2015 Petr Shevtsov (petr.shevtsov@gmail.com)
 * MIT-licensed (http://opensource.org/licenses/MIT)
 */

/*global Dygraph:false */
/*jshint globalstrict: true */
Dygraph.Plugins.Crosshair = (function() {
  "use strict";

  /**
   * Creates the crosshair
   *
   * @constructor
   */

  var crosshair = function(opt_options) {
    this.canvas_ = document.createElement("canvas");
    opt_options = opt_options || {};
    this.direction_ = opt_options.direction || null;
  };

  crosshair.prototype.toString = function() {
    return "Crosshair Plugin";
  };

  /**
   * @param {Dygraph} g Graph instance.
   * @return {object.<string, function(ev)>} Mapping of event names to callbacks.
   */
  crosshair.prototype.activate = function(g) {
    g.graphDiv.appendChild(this.canvas_);

    return {
      select: this.select,
      deselect: this.deselect
    };
  };

  crosshair.prototype.select = function(e) {
    if (this.direction_ === null) {
      return;
    }

    var width = e.dygraph.width_;
    var height = e.dygraph.height_;
    this.canvas_.width = width;
    this.canvas_.height = height;
    this.canvas_.style.width = width + "px";    // for IE
    this.canvas_.style.height = height + "px";  // for IE

    var ctx = this.canvas_.getContext("2d");
    ctx.clearRect(0, 0, width, height);
    ctx.strokeStyle = "rgba(0, 0, 0,0.3)";
    ctx.beginPath();

    var canvasx = Math.floor(e.dygraph.selPoints_[0].canvasx) + 0.5; // crisper rendering

    if (this.direction_ === "vertical" || this.direction_ === "both") {
      ctx.moveTo(canvasx, 0);
      ctx.lineTo(canvasx, height);
    }

    if (this.direction_ === "horizontal" || this.direction_ === "both") {
      for (var i = 0; i < e.dygraph.selPoints_.length; i++) {
        var canvasy = Math.floor(e.dygraph.selPoints_[i].canvasy) + 0.5; // crisper rendering
        ctx.moveTo(0, canvasy);
        ctx.lineTo(width, canvasy);
      }
    }

    ctx.stroke();
    ctx.closePath();
  };

  crosshair.prototype.deselect = function(e) {
    var ctx = this.canvas_.getContext("2d");
    ctx.clearRect(0, 0, this.canvas_.width, this.canvas_.height);
  };

  crosshair.prototype.destroy = function() {
    this.canvas_ = null;
  };

  return crosshair;
})();
;
/**
 * gridstack.js 1.1.2
 * https://gridstackjs.com/
 * (c) 2014-2020 Alain Dumesny, Dylan Weiss, Pavel Reznikov
 * gridstack.js may be freely distributed under the MIT license.
 * @preserve
*/
(function(factory) {
  /* [alain] we compile jquery with our code, so no need to 'load' externally
  if (typeof define === 'function' && define.amd) {
    define(['jquery', 'exports'], factory);
  } else if (typeof exports !== 'undefined') {
    var jQueryModule;

    try { jQueryModule = require('jquery'); } catch (e) {}

    factory(jQueryModule || window.jQuery, exports);
  } else */{
    factory(window.jQuery, window);
  }
})(function($, scope) {

  // checks for obsolete method names
  var obsolete = function(f, oldName, newName, rev) {
    var wrapper = function() {
      console.warn('gridstack.js: Function `' + oldName + '` is deprecated in ' + rev + ' and has been replaced ' +
      'with `' + newName + '`. It will be **completely** removed in v1.0');
      return f.apply(this, arguments);
    };
    wrapper.prototype = f.prototype;

    return wrapper;
  };

  // checks for obsolete grid options (can be used for any fields, but msg is about options)
  var obsoleteOpts = function(opts, oldName, newName, rev) {
    if (opts[oldName] !== undefined) {
      opts[newName] = opts[oldName];
      console.warn('gridstack.js: Option `' + oldName + '` is deprecated in ' + rev + ' and has been replaced with `' +
        newName + '`. It will be **completely** removed in v1.0');
    }
  };

  // checks for obsolete grid options which are gone
  var obsoleteOptsDel = function(opts, oldName, rev, info) {
    if (opts[oldName] !== undefined) {
      console.warn('gridstack.js: Option `' + oldName + '` is deprecated in ' + rev + info);
    }
  };

  // checks for obsolete Jquery element attributes
  var obsoleteAttr = function(el, oldName, newName, rev) {
    var oldAttr = el.attr(oldName);
    if (oldAttr !== undefined) {
      el.attr(newName, oldAttr);
      console.warn('gridstack.js: attribute `' + oldName + '`=' + oldAttr + ' is deprecated on this object in ' + rev + ' and has been replaced with `' +
        newName + '`. It will be **completely** removed in v1.0');
    }
  };

  var Utils = {

    isIntercepted: function(a, b) {
      return !(a.x + a.width <= b.x || b.x + b.width <= a.x || a.y + a.height <= b.y || b.y + b.height <= a.y);
    },

    sort: function(nodes, dir, column) {
      if (!column) {
        var widths = nodes.map(function(node) { return node.x + node.width; });
        column = Math.max.apply(Math, widths);
      }

      if (dir === -1)
        return Utils.sortBy(nodes, function(n) { return -(n.x + n.y * column); });
      else
        return Utils.sortBy(nodes, function(n) { return (n.x + n.y * column); });
    },

    createStylesheet: function(id, parent) {
      var style = document.createElement('style');
      style.setAttribute('type', 'text/css');
      style.setAttribute('data-gs-style-id', id);
      if (style.styleSheet) {
        style.styleSheet.cssText = '';
      } else {
        style.appendChild(document.createTextNode(''));
      }
      if (!parent) { parent = document.getElementsByTagName('head')[0]; } // default to head
      parent.insertBefore(style, parent.firstChild);
      return style.sheet;
    },

    removeStylesheet: function(id) {
      $('STYLE[data-gs-style-id=' + id + ']').remove();
    },

    insertCSSRule: function(sheet, selector, rules, index) {
      if (typeof sheet.insertRule === 'function') {
        sheet.insertRule(selector + '{' + rules + '}', index);
      } else if (typeof sheet.addRule === 'function') {
        sheet.addRule(selector, rules, index);
      }
    },

    toBool: function(v) {
      if (typeof v === 'boolean') {
        return v;
      }
      if (typeof v === 'string') {
        v = v.toLowerCase();
        return !(v === '' || v === 'no' || v === 'false' || v === '0');
      }
      return Boolean(v);
    },

    _collisionNodeCheck: function(n) {
      return n !== this.node && Utils.isIntercepted(n, this.nn);
    },

    _didCollide: function(bn) {
      return Utils.isIntercepted({x: this.n.x, y: this.newY, width: this.n.width, height: this.n.height}, bn);
    },

    _isAddNodeIntercepted: function(n) {
      return Utils.isIntercepted({x: this.x, y: this.y, width: this.node.width, height: this.node.height}, n);
    },

    parseHeight: function(val) {
      var height = val;
      var heightUnit = 'px';
      if (height && typeof height === 'string') {
        var match = height.match(/^(-[0-9]+\.[0-9]+|[0-9]*\.[0-9]+|-[0-9]+|[0-9]+)(px|em|rem|vh|vw|%)?$/);
        if (!match) {
          throw new Error('Invalid height');
        }
        heightUnit = match[2] || 'px';
        height = parseFloat(match[1]);
      }
      return {height: height, unit: heightUnit};
    },

    without:  function(array, item) {
      var index = array.indexOf(item);

      if (index !== -1) {
        array = array.slice(0);
        array.splice(index, 1);
      }

      return array;
    },

    sortBy: function(array, getter) {
      return array.slice(0).sort(function(left, right) {
        var valueLeft = getter(left);
        var valueRight = getter(right);

        if (valueRight === valueLeft) {
          return 0;
        }

        return valueLeft > valueRight ? 1 : -1;
      });
    },

    defaults: function(target) {
      var sources = Array.prototype.slice.call(arguments, 1);

      sources.forEach(function(source) {
        for (var prop in source) {
          if (Object.prototype.hasOwnProperty.call(source, prop) && (!Object.prototype.hasOwnProperty.call(target, prop) || target[prop] === undefined)) {
            target[prop] = source[prop];
          }
        }
      });

      return target;
    },

    clone: function(target) {
      return $.extend({}, target);
    },

    throttle: function(callback, delay) {
      var isWaiting = false;

      return function() {
        if (!isWaiting) {
          callback.apply(this, arguments);
          isWaiting = true;
          setTimeout(function() { isWaiting = false; }, delay);
        }
      };
    },

    removePositioningStyles: function(el) {
      var style = el[0].style;
      if (style.position) {
        style.removeProperty('position');
      }
      if (style.left) {
        style.removeProperty('left');
      }
      if (style.top) {
        style.removeProperty('top');
      }
      if (style.width) {
        style.removeProperty('width');
      }
      if (style.height) {
        style.removeProperty('height');
      }
    },
    getScrollParent: function(el) {
      var returnEl;
      if (el === null) {
        returnEl = null;
      } else if (el.scrollHeight > el.clientHeight) {
        returnEl = el;
      } else {
        returnEl = Utils.getScrollParent(el.parentNode);
      }
      return returnEl;
    },
    updateScrollPosition: function(el, ui, distance) {
      // is widget in view?
      var rect = el.getBoundingClientRect();
      var innerHeightOrClientHeight = (window.innerHeight || document.documentElement.clientHeight);
      if (rect.top < 0 ||
        rect.bottom > innerHeightOrClientHeight
      ) {
        // set scrollTop of first parent that scrolls
        // if parent is larger than el, set as low as possible
        // to get entire widget on screen
        var offsetDiffDown = rect.bottom - innerHeightOrClientHeight;
        var offsetDiffUp = rect.top;
        var scrollEl = Utils.getScrollParent(el);
        if (scrollEl !== null) {
          var prevScroll = scrollEl.scrollTop;
          if (rect.top < 0 && distance < 0) {
            // moving up
            if (el.offsetHeight > innerHeightOrClientHeight) {
              scrollEl.scrollTop += distance;
            } else {
              scrollEl.scrollTop += Math.abs(offsetDiffUp) > Math.abs(distance) ? distance : offsetDiffUp;
            }
          } else if (distance > 0) {
            // moving down
            if (el.offsetHeight > innerHeightOrClientHeight) {
              scrollEl.scrollTop += distance;
            } else {
              scrollEl.scrollTop += offsetDiffDown > distance ? distance : offsetDiffDown;
            }
          }
          // move widget y by amount scrolled
          ui.position.top += scrollEl.scrollTop - prevScroll;
        }
      }
    }
  };

  /**
  * @class GridStackDragDropPlugin
  * Base class for drag'n'drop plugin.
  */
  function GridStackDragDropPlugin(grid) {
    this.grid = grid;
  }

  GridStackDragDropPlugin.registeredPlugins = [];

  GridStackDragDropPlugin.registerPlugin = function(pluginClass) {
    GridStackDragDropPlugin.registeredPlugins.push(pluginClass);
  };

  GridStackDragDropPlugin.prototype.resizable = function(el, opts) {
    return this;
  };

  GridStackDragDropPlugin.prototype.draggable = function(el, opts) {
    return this;
  };

  GridStackDragDropPlugin.prototype.droppable = function(el, opts) {
    return this;
  };

  GridStackDragDropPlugin.prototype.isDroppable = function(el) {
    return false;
  };

  GridStackDragDropPlugin.prototype.on = function(el, eventName, callback) {
    return this;
  };


  var idSeq = 0;

  var GridStackEngine = function(column, onchange, float, maxRow, items) {
    this.column = column || 12;
    this.float = float || false;
    this.maxRow = maxRow || 0;

    this.nodes = items || [];
    this.onchange = onchange || function() {};

    this._addedNodes = [];
    this._removedNodes = [];
    this._batchMode = false;
  };

  GridStackEngine.prototype.batchUpdate = function() {
    if (this._batchMode) return;
    this._batchMode = true;
    this._prevFloat = this.float;
    this.float = true; // let things go anywhere for now... commit() will restore and possibly reposition
  };

  GridStackEngine.prototype.commit = function() {
    if (!this._batchMode) return;
    this._batchMode = false;
    this.float = this._prevFloat;
    delete this._prevFloat;
    this._packNodes();
    this._notify();
  };

  // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272
  GridStackEngine.prototype.getNodeDataByDOMEl = function(el) {
    return this.nodes.find(function(node) { return el === node.el });
  };

  GridStackEngine.prototype._fixCollisions = function(node) {
    var self = this;
    this._sortNodes(-1);

    var nn = node;
    var hasLocked = Boolean(this.nodes.find(function(n) { return n.locked; }));
    if (!this.float && !hasLocked) {
      nn = {x: 0, y: node.y, width: this.column, height: node.height};
    }
    while (true) {
      var collisionNode = this.nodes.find(Utils._collisionNodeCheck, {node: node, nn: nn});
      if (!collisionNode) { return; }
      var moved;
      if (collisionNode.locked) {
        // if colliding with a locked item, move ourself instead
        moved = this.moveNode(node, node.x, collisionNode.y + collisionNode.height,
          node.width, node.height, true);
      } else {
        moved = this.moveNode(collisionNode, collisionNode.x, node.y + node.height,
          collisionNode.width, collisionNode.height, true);
      }
      if (!moved) { return; } // break inf loop if we couldn't move after all (ex: maxRow, fixed)
    }
  };

  GridStackEngine.prototype.isAreaEmpty = function(x, y, width, height) {
    var nn = {x: x || 0, y: y || 0, width: width || 1, height: height || 1};
    var collisionNode = this.nodes.find(function(n) {
      return Utils.isIntercepted(n, nn);
    });
    return !collisionNode;
  };

  GridStackEngine.prototype._sortNodes = function(dir) {
    this.nodes = Utils.sort(this.nodes, dir, this.column);
  };

  GridStackEngine.prototype._packNodes = function() {
    this._sortNodes();

    if (this.float) {
      this.nodes.forEach(function(n, i) {
        if (n._updating || n._packY === undefined || n.y === n._packY) {
          return;
        }

        var newY = n.y;
        while (newY >= n._packY) {
          var collisionNode = this.nodes
            .slice(0, i)
            .find(Utils._didCollide, {n: n, newY: newY});

          if (!collisionNode) {
            n._dirty = true;
            n.y = newY;
          }
          --newY;
        }
      }, this);
    } else {
      this.nodes.forEach(function(n, i) {
        if (n.locked) { return; }
        while (n.y > 0) {
          var newY = n.y - 1;
          var canBeMoved = i === 0;

          if (i > 0) {
            var collisionNode = this.nodes
              .slice(0, i)
              .find(Utils._didCollide, {n: n, newY: newY});
            canBeMoved = collisionNode === undefined;
          }

          if (!canBeMoved) { break; }
          // Note: must be dirty (from last position) for GridStack::OnChange CB to update positions
          // and move items back. The user 'change' CB should detect changes from the original
          // starting position instead.
          n._dirty = (n.y !== newY);
          n.y = newY;
        }
      }, this);
    }
  };

  GridStackEngine.prototype._prepareNode = function(node, resizing) {
    node = node || {};
    // if we're missing position, have the grid position us automatically (before we set them to 0,0)
    if (node.x === undefined || node.y === undefined || node.x === null || node.y === null) {
      node.autoPosition = true;
    }

    // assign defaults for missing required fields
    var defaults = {width: 1, height: 1, x: 0, y: 0};
    node = Utils.defaults(node, defaults);

    // convert any strings over
    node.x = parseInt(node.x);
    node.y = parseInt(node.y);
    node.width = parseInt(node.width);
    node.height = parseInt(node.height);
    node.autoPosition = node.autoPosition || false;
    node.noResize = node.noResize || false;
    node.noMove = node.noMove || false;

    // check for NaN (in case messed up strings were passed. can't do parseInt() || defaults.x above as 0 is valid #)
    if (Number.isNaN(node.x))      { node.x = defaults.x; node.autoPosition = true; }
    if (Number.isNaN(node.y))      { node.y = defaults.y; node.autoPosition = true; }
    if (Number.isNaN(node.width))  { node.width = defaults.width; }
    if (Number.isNaN(node.height)) { node.height = defaults.height; }

    if (node.maxWidth !== undefined) { node.width = Math.min(node.width, node.maxWidth); }
    if (node.maxHeight !== undefined) { node.height = Math.min(node.height, node.maxHeight); }
    if (node.minWidth !== undefined) { node.width = Math.max(node.width, node.minWidth); }
    if (node.minHeight !== undefined) { node.height = Math.max(node.height, node.minHeight); }

    if (node.width > this.column) {
      node.width = this.column;
    } else if (node.width < 1) {
      node.width = 1;
    }
    if (this.maxRow && node.height > this.maxRow) {
      node.height = this.maxRow;
    } else if (node.height < 1) {
      node.height = 1;
    }

    if (node.x < 0) {
      node.x = 0;
    }
    if (node.y < 0) {
      node.y = 0;
    }

    if (node.x + node.width > this.column) {
      if (resizing) {
        node.width = this.column - node.x;
      } else {
        node.x = this.column - node.width;
      }
    }
    if (this.maxRow && node.y + node.height > this.maxRow) {
      if (resizing) {
        node.height = this.maxRow - node.y;
      } else {
        node.y = this.maxRow - node.height;
      }
    }

    return node;
  };

  GridStackEngine.prototype._notify = function() {
    if (this._batchMode) { return; }
    var args = Array.prototype.slice.call(arguments, 0);
    args[0] = (args[0] === undefined ? [] : (Array.isArray(args[0]) ? args[0] : [args[0]]) );
    args[1] = (args[1] === undefined ? true : args[1]);
    var dirtyNodes = args[0].concat(this.getDirtyNodes());
    this.onchange(dirtyNodes, args[1]);
  };

  GridStackEngine.prototype.cleanNodes = function() {
    if (this._batchMode) { return; }
    this.nodes.forEach(function(n) { delete n._dirty; });
  };

  GridStackEngine.prototype.getDirtyNodes = function(verify) {
    // compare original X,Y,W,H (or entire node?) instead as _dirty can be a temporary state
    if (verify) {
      var dirtNodes = [];
      this.nodes.forEach(function (n) {
        if (n._dirty) {
          if (n.y === n._origY && n.x === n._origX && n.width === n._origW && n.height === n._origH) {
            delete n._dirty;
          } else {
            dirtNodes.push(n);
          }
        }
      });
      return dirtNodes;
    }

    return this.nodes.filter(function(n) { return n._dirty; });
  };

  GridStackEngine.prototype.addNode = function(node, triggerAddEvent) {
    node = this._prepareNode(node);

    node._id = node._id || ++idSeq;

    if (node.autoPosition) {
      this._sortNodes();

      for (var i = 0;; ++i) {
        var x = i % this.column;
        var y = Math.floor(i / this.column);
        if (x + node.width > this.column) {
          continue;
        }
        if (!this.nodes.find(Utils._isAddNodeIntercepted, {x: x, y: y, node: node})) {
          node.x = x;
          node.y = y;
          delete node.autoPosition; // found our slot
          break;
        }
      }
    }

    this.nodes.push(node);
    if (triggerAddEvent) {
      this._addedNodes.push(node);
    }

    this._fixCollisions(node);
    this._packNodes();
    this._notify();
    return node;
  };

  GridStackEngine.prototype.removeNode = function(node, detachNode) {
    detachNode = (detachNode === undefined ? true : detachNode);
    this._removedNodes.push(node);
    node._id = null; // hint that node is being removed
    this.nodes = Utils.without(this.nodes, node);
    this._packNodes();
    this._notify(node, detachNode);
  };

  GridStackEngine.prototype.removeAll = function(detachNode) {
    delete this._layouts;
    if (this.nodes.length === 0) { return; }
    detachNode = (detachNode === undefined ? true : detachNode);
    this.nodes.forEach(function(n) { n._id = null; }); // hint that node is being removed
    this._removedNodes = this.nodes;
    this.nodes = [];
    this._notify(this._removedNodes, detachNode);
  };

  GridStackEngine.prototype.canMoveNode = function(node, x, y, width, height) {
    if (!this.isNodeChangedPosition(node, x, y, width, height)) {
      return false;
    }
    var hasLocked = Boolean(this.nodes.find(function(n) { return n.locked; }));

    if (!this.maxRow && !hasLocked) {
      return true;
    }

    var clonedNode;
    var clone = new GridStackEngine(
      this.column,
      null,
      this.float,
      0,
      this.nodes.map(function(n) {
        if (n === node) {
          clonedNode = $.extend({}, n);
          return clonedNode;
        }
        return $.extend({}, n);
      }));

    if (!clonedNode) {  return true;}

    clone.moveNode(clonedNode, x, y, width, height);

    var res = true;

    if (hasLocked) {
      res &= !Boolean(clone.nodes.find(function(n) {
        return n !== clonedNode && Boolean(n.locked) && Boolean(n._dirty);
      }));
    }
    if (this.maxRow) {
      res &= clone.getRow() <= this.maxRow;
    }

    return res;
  };

  GridStackEngine.prototype.canBePlacedWithRespectToHeight = function(node) {
    if (!this.maxRow) {
      return true;
    }

    var clone = new GridStackEngine(
      this.column,
      null,
      this.float,
      0,
      this.nodes.map(function(n) { return $.extend({}, n); }));
    clone.addNode(node);
    return clone.getRow() <= this.maxRow;
  };

  GridStackEngine.prototype.isNodeChangedPosition = function(node, x, y, width, height) {
    if (typeof x !== 'number') { x = node.x; }
    if (typeof y !== 'number') { y = node.y; }
    if (typeof width !== 'number') { width = node.width; }
    if (typeof height !== 'number') { height = node.height; }

    if (node.maxWidth !== undefined) { width = Math.min(width, node.maxWidth); }
    if (node.maxHeight !== undefined) { height = Math.min(height, node.maxHeight); }
    if (node.minWidth !== undefined) { width = Math.max(width, node.minWidth); }
    if (node.minHeight !== undefined) { height = Math.max(height, node.minHeight); }

    if (node.x === x && node.y === y && node.width === width && node.height === height) {
      return false;
    }
    return true;
  };

  GridStackEngine.prototype.moveNode = function(node, x, y, width, height, noPack) {
    if (node.locked) { return null; }
    if (typeof x !== 'number') { x = node.x; }
    if (typeof y !== 'number') { y = node.y; }
    if (typeof width !== 'number') { width = node.width; }
    if (typeof height !== 'number') { height = node.height; }

    // constrain the passed in values and check if we're still changing our node
    var resizing = (node.width !== width || node.height !== height);
    var nn = { x: x, y: y, width: width, height: height,
      maxWidth: node.maxWidth, maxHeight: node.maxHeight, minWidth: node.minWidth, minHeight: node.minHeight};
    nn = this._prepareNode(nn, resizing);
    if (node.x === nn.x && node.y === nn.y && node.width === nn.width && node.height === nn.height) {
      return null;
    }

    node._dirty = true;

    node.x = node.lastTriedX = nn.x;
    node.y = node.lastTriedY = nn.y;
    node.width = node.lastTriedWidth = nn.width;
    node.height = node.lastTriedHeight = nn.height;

    this._fixCollisions(node);
    if (!noPack) {
      this._packNodes();
      this._notify();
    }
    return node;
  };

  GridStackEngine.prototype.getRow = function() {
    return this.nodes.reduce(function(memo, n) { return Math.max(memo, n.y + n.height); }, 0);
  };

  GridStackEngine.prototype.beginUpdate = function(node) {
    if (node._updating) return;
    node._updating = true;
    this.nodes.forEach(function(n) { n._packY = n.y; });
  };

  GridStackEngine.prototype.endUpdate = function() {
    var n = this.nodes.find(function(n) { return n._updating; });
    if (n) {
      n._updating = false;
      this.nodes.forEach(function(n) { delete n._packY; });
    }
  };

  /**
   * Construct a grid item from the given element and options
   * @param {GridStackElement} el
   * @param {GridstackOptions} opts
   */
  var GridStack = function(el, opts) {
    var self = this;
    var oneColumnMode, _prevColumn, isAutoCellHeight;

    opts = opts || {};

    this.$el = $(el); // TODO: legacy code
    this.el = this.$el.get(0); // exposed HTML element to the user

    obsoleteOpts(opts, 'width', 'column', 'v0.5.3');
    obsoleteOpts(opts, 'height', 'maxRow', 'v0.5.3');
    obsoleteOptsDel(opts, 'oneColumnModeClass', 'v0.6.3', '. Use class `.grid-stack-1` instead');

    // container attributes
    obsoleteAttr(this.$el, 'data-gs-width', 'data-gs-column', 'v0.5.3');
    obsoleteAttr(this.$el, 'data-gs-height', 'data-gs-max-row', 'v0.5.3');
    obsoleteAttr(this.$el, 'data-gs-current-height', 'data-gs-current-row', 'v1.0.0');

    opts.itemClass = opts.itemClass || 'grid-stack-item';
    var isNested = this.$el.closest('.' + opts.itemClass).length > 0;

    // if row property exists, replace minRow and maxRow instead
    if (opts.row) {
      opts.minRow = opts.maxRow = opts.row;
      delete opts.row;
    }
    var rowAttr = parseInt(this.$el.attr('data-gs-row'));

    // elements attributes override any passed options (like CSS style) - merge the two together
    this.opts = Utils.defaults(opts, {
      column: parseInt(this.$el.attr('data-gs-column')) || 12,
      minRow: rowAttr ? rowAttr : parseInt(this.$el.attr('data-gs-min-row')) || 0,
      maxRow: rowAttr ? rowAttr : parseInt(this.$el.attr('data-gs-max-row')) || 0,
      itemClass: 'grid-stack-item',
      placeholderClass: 'grid-stack-placeholder',
      placeholderText: '',
      handle: '.grid-stack-item-content',
      handleClass: null,
      cellHeight: 60,
      verticalMargin: 20,
      auto: true,
      minWidth: 768,
      float: false,
      staticGrid: false,
      _class: 'grid-stack-instance-' + (Math.random() * 10000).toFixed(0),
      animate: Boolean(this.$el.attr('data-gs-animate')) || false,
      alwaysShowResizeHandle: opts.alwaysShowResizeHandle || false,
      resizable: Utils.defaults(opts.resizable || {}, {
        autoHide: !(opts.alwaysShowResizeHandle || false),
        handles: 'se'
      }),
      draggable: Utils.defaults(opts.draggable || {}, {
        handle: (opts.handleClass ? '.' + opts.handleClass : (opts.handle ? opts.handle : '')) ||
          '.grid-stack-item-content',
        scroll: false,
        appendTo: 'body'
      }),
      disableDrag: opts.disableDrag || false,
      disableResize: opts.disableResize || false,
      rtl: 'auto',
      removable: false,
      removableOptions: Utils.defaults(opts.removableOptions || {}, {
        accept: '.' + opts.itemClass
      }),
      removeTimeout: 2000,
      verticalMarginUnit: 'px',
      cellHeightUnit: 'px',
      disableOneColumnMode: opts.disableOneColumnMode || false,
      oneColumnModeDomSort: opts.oneColumnModeDomSort,
      ddPlugin: null
    });

    if (this.opts.ddPlugin === false) {
      this.opts.ddPlugin = GridStackDragDropPlugin;
    } else if (this.opts.ddPlugin === null) {
      this.opts.ddPlugin = GridStackDragDropPlugin.registeredPlugins[0] || GridStackDragDropPlugin;
    }

    this.dd = new this.opts.ddPlugin(this);

    if (this.opts.rtl === 'auto') {
      this.opts.rtl = this.$el.css('direction') === 'rtl';
    }

    if (this.opts.rtl) {
      this.$el.addClass('grid-stack-rtl');
    }

    this.opts.isNested = isNested;

    isAutoCellHeight = (this.opts.cellHeight === 'auto');
    if (isAutoCellHeight) {
      // make the cell square initially
      self.cellHeight(self.cellWidth(), true);
    } else {
      this.cellHeight(this.opts.cellHeight, true);
    }
    this.verticalMargin(this.opts.verticalMargin, true);

    this.$el.addClass(this.opts._class);

    this._setStaticClass();

    if (isNested) {
      this.$el.addClass('grid-stack-nested');
    }

    this._initStyles();

    this.engine = new GridStackEngine(this.opts.column, function(nodes, detachNode) {
      detachNode = (detachNode === undefined ? true : detachNode);
      var maxHeight = 0;
      this.nodes.forEach(function(n) {
        maxHeight = Math.max(maxHeight, n.y + n.height);
      });
      nodes.forEach(function(n) {
        if (detachNode && n._id === null) {
          if (n.el) {
            $(n.el).remove();
          }
        } else {
          $(n.el)
            .attr('data-gs-x', n.x)
            .attr('data-gs-y', n.y)
            .attr('data-gs-width', n.width)
            .attr('data-gs-height', n.height);
        }
      });
      self._updateStyles(maxHeight + 10);
    }, this.opts.float, this.opts.maxRow);

    if (this.opts.auto) {
      var elements = [];
      var _this = this;
      this.$el.children('.' + this.opts.itemClass + ':not(.' + this.opts.placeholderClass + ')')
        .each(function(index, el) {
          el = $(el);
          var x = parseInt(el.attr('data-gs-x'));
          var y = parseInt(el.attr('data-gs-y'));
          elements.push({
            el: el.get(0),
            // if x,y are missing (autoPosition) add them to end of list - but keep their respective DOM order
            i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * _this.opts.column
          });
        });
      Utils.sortBy(elements, function(x) { return x.i; }).forEach(function(item) {
        this._prepareElement(item.el);
      }, this);
    }
    this.engine._saveInitial(); // initial start of items

    this.setAnimation(this.opts.animate);

    this.placeholder = $(
      '<div class="' + this.opts.placeholderClass + ' ' + this.opts.itemClass + '">' +
      '<div class="placeholder-content">' + this.opts.placeholderText + '</div></div>').hide();

    this._updateContainerHeight();

    this._updateHeightsOnResize = Utils.throttle(function() {
      self.cellHeight(self.cellWidth(), false);
    }, 100);

    /**
     * called when we are being resized - check if the one Column Mode needs to be turned on/off
     * and remember the prev columns we used.
     */
    this.onResizeHandler = function() {
      if (isAutoCellHeight) {
        self._updateHeightsOnResize();
      }

      if (!self.opts.disableOneColumnMode && (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) <= self.opts.minWidth) {
        if (self.oneColumnMode) { return }
        self.oneColumnMode = true;
        self.column(1);
      } else {
        if (!self.oneColumnMode) { return }
        self.oneColumnMode = false;
        self.column(self._prevColumn);
      }
    };

    $(window).resize(this.onResizeHandler);
    this.onResizeHandler();

    if (!self.opts.staticGrid && typeof self.opts.removable === 'string') {
      var trashZone = $(self.opts.removable);
      if (!this.dd.isDroppable(trashZone)) {
        this.dd.droppable(trashZone, self.opts.removableOptions);
      }
      this.dd
        .on(trashZone, 'dropover', function(event, ui) {
          var el = $(ui.draggable);
          var node = el.data('_gridstack_node');
          if (!node || node._grid !== self) {
            return;
          }
          el.data('inTrashZone', true);
          self._setupRemovingTimeout(el);
        })
        .on(trashZone, 'dropout', function(event, ui) {
          var el = $(ui.draggable);
          var node = el.data('_gridstack_node');
          if (!node || node._grid !== self) {
            return;
          }
          el.data('inTrashZone', false);
          self._clearRemovingTimeout(el);
        });
    }

    if (!self.opts.staticGrid && self.opts.acceptWidgets) {
      var draggingElement = null;

      var onDrag = function(event, ui) {
        var el = draggingElement;
        var node = el.data('_gridstack_node');
        var pos = self.getCellFromPixel({left: event.pageX, top: event.pageY}, true);
        var x = Math.max(0, pos.x);
        var y = Math.max(0, pos.y);
        if (!node._added) {
          node._added = true;

          node.el = el.get(0);
          node.autoPosition = true;
          node.x = x;
          node.y = y;
          self.engine.cleanNodes();
          self.engine.beginUpdate(node);
          self.engine.addNode(node);

          self.$el.append(self.placeholder);
          self.placeholder
            .attr('data-gs-x', node.x)
            .attr('data-gs-y', node.y)
            .attr('data-gs-width', node.width)
            .attr('data-gs-height', node.height)
            .show();
          node.el = self.placeholder.get(0);
          node._beforeDragX = node.x;
          node._beforeDragY = node.y;

          self._updateContainerHeight();
        }
        if (!self.engine.canMoveNode(node, x, y)) {
          return;
        }
        self.engine.moveNode(node, x, y);
        self._updateContainerHeight();
      };

      this.dd
        .droppable(self.$el, {
          accept: function(el) {
            el = $(el);
            var node = el.data('_gridstack_node');
            if (node && node._grid === self) {
              return false;
            }
            return el.is(self.opts.acceptWidgets === true ? '.grid-stack-item' : self.opts.acceptWidgets);
          }
        })
        .on(self.$el, 'dropover', function(event, ui) {
          var el = $(ui.draggable);
          var width, height;

          // see if we already have a node with widget/height and check for attributes
          var origNode = el.data('_gridstack_node');
          if (!origNode || !origNode.width || !origNode.height) {
            var w = parseInt(el.attr('data-gs-width'));
            if (w > 0) { origNode = origNode || {}; origNode.width = w; }
            var h = parseInt(el.attr('data-gs-height'));
            if (h > 0) { origNode = origNode || {}; origNode.height = h; }
          }

          // if not calculate the grid size based on element outer size
          // height: Each row is cellHeight + verticalMargin, until last one which has no margin below
          var cellWidth = self.cellWidth();
          var cellHeight = self.cellHeight();
          var verticalMargin = self.opts.verticalMargin;
          width = origNode && origNode.width ? origNode.width : Math.ceil(el.outerWidth() / cellWidth);
          height = origNode && origNode.height ? origNode.height : Math.round((el.outerHeight() + verticalMargin) / (cellHeight + verticalMargin));

          draggingElement = el;

          var node = self.engine._prepareNode({width: width, height: height, _added: false, _temporary: true});
          node.isOutOfGrid = true;
          el.data('_gridstack_node', node);
          el.data('_gridstack_node_orig', origNode);

          el.on('drag', onDrag);
          return false; // prevent parent from receiving msg (which may be grid as well)
        })
        .on(self.$el, 'dropout', function(event, ui) {
          // jquery-ui bug. Must verify widget is being dropped out
          // check node variable that gets set when widget is out of grid
          var el = $(ui.draggable);
          if (!el.data('_gridstack_node')) {
            return;
          }
          var node = el.data('_gridstack_node');
          if (!node.isOutOfGrid) {
            return;
          }
          el.unbind('drag', onDrag);
          node.el = null;
          self.engine.removeNode(node);
          self.placeholder.detach();
          self._updateContainerHeight();
          el.data('_gridstack_node', el.data('_gridstack_node_orig'));
          return false; // prevent parent from receiving msg (which may be grid as well)
        })
        .on(self.$el, 'drop', function(event, ui) {
          self.placeholder.detach();

          var node = $(ui.draggable).data('_gridstack_node');
          node.isOutOfGrid = false;
          node._grid = self;
          var el = $(ui.draggable).clone(false);
          el.data('_gridstack_node', node);
          var originalNode = $(ui.draggable).data('_gridstack_node_orig');
          if (originalNode !== undefined && originalNode._grid !== undefined) {
            originalNode._grid._triggerRemoveEvent();
          }
          $(ui.helper).remove();
          node.el = el.get(0);
          self.placeholder.hide();
          Utils.removePositioningStyles(el);
          el.find('div.ui-resizable-handle').remove();

          el
            .attr('data-gs-x', node.x)
            .attr('data-gs-y', node.y)
            .attr('data-gs-width', node.width)
            .attr('data-gs-height', node.height)
            .addClass(self.opts.itemClass)
            .enableSelection()
            .removeData('draggable')
            .removeClass('ui-draggable ui-draggable-dragging ui-draggable-disabled')
            .unbind('drag', onDrag);
          self.$el.append(el);
          self._prepareElementsByNode(el, node);
          self._updateContainerHeight();
          self.engine._addedNodes.push(node);
          self._triggerAddEvent();
          self._triggerChangeEvent();

          self.engine.endUpdate();
          $(ui.draggable).unbind('drag', onDrag);
          $(ui.draggable).removeData('_gridstack_node');
          $(ui.draggable).removeData('_gridstack_node_orig');
          self.$el.trigger('dropped', [originalNode, node]);
          return false; // prevent parent from receiving msg (which may be grid as well)
        });
    }
  };

  GridStack.prototype._triggerChangeEvent = function(/*forceTrigger*/) {
    if (this.engine._batchMode) { return; }
    var elements = this.engine.getDirtyNodes(true); // verify they really changed
    if (elements && elements.length) {
      this.engine._layoutsNodesChange(elements);
      this._triggerEvent('change', elements);
    }
    this.engine._saveInitial(); // we called, now reset initial values & dirty flags
  };

  GridStack.prototype._triggerAddEvent = function() {
    if (this.engine._batchMode) { return; }
    if (this.engine._addedNodes && this.engine._addedNodes.length > 0) {
      this.engine._layoutsNodesChange(this.engine._addedNodes);
      // prevent added nodes from also triggering 'change' event (which is called next)
      this.engine._addedNodes.forEach(function (n) { delete n._dirty; });
      this._triggerEvent('added', this.engine._addedNodes);
      this.engine._addedNodes = [];
    }
  };

  GridStack.prototype._triggerRemoveEvent = function() {
    if (this.engine._batchMode) { return; }
    if (this.engine._removedNodes && this.engine._removedNodes.length > 0) {
      this._triggerEvent('removed', this.engine._removedNodes);
      this.engine._removedNodes = [];
    }
  };

  GridStack.prototype._triggerEvent = function(name, data) {
    var event = new CustomEvent(name, {detail: data});
    this.el.dispatchEvent(event);
  };

  GridStack.prototype._initStyles = function() {
    if (this._stylesId) {
      Utils.removeStylesheet(this._stylesId);
    }
    this._stylesId = 'gridstack-style-' + (Math.random() * 100000).toFixed();
    // insert style to parent (instead of 'head') to support WebComponent
    this._styles = Utils.createStylesheet(this._stylesId, this.el.parentNode);
    if (this._styles !== null) {
      this._styles._max = 0;
    }
  };

  GridStack.prototype._updateStyles = function(maxHeight) {
    if (this._styles === null || this._styles === undefined) {
      return;
    }

    var prefix = '.' + this.opts._class + ' .' + this.opts.itemClass;
    var self = this;
    var getHeight;

    if (maxHeight === undefined) {
      maxHeight = this._styles._max;
    }

    this._initStyles();
    this._updateContainerHeight();
    if (!this.opts.cellHeight) { // The rest will be handled by CSS
      return ;
    }
    if (this._styles._max !== 0 && maxHeight <= this._styles._max) { // Keep this._styles._max increasing
      return ;
    }

    if (!this.opts.verticalMargin || this.opts.cellHeightUnit === this.opts.verticalMarginUnit) {
      getHeight = function(nbRows, nbMargins) {
        return (self.opts.cellHeight * nbRows + self.opts.verticalMargin * nbMargins) +
          self.opts.cellHeightUnit;
      };
    } else {
      getHeight = function(nbRows, nbMargins) {
        if (!nbRows || !nbMargins) {
          return (self.opts.cellHeight * nbRows + self.opts.verticalMargin * nbMargins) +
            self.opts.cellHeightUnit;
        }
        return 'calc(' + ((self.opts.cellHeight * nbRows) + self.opts.cellHeightUnit) + ' + ' +
          ((self.opts.verticalMargin * nbMargins) + self.opts.verticalMarginUnit) + ')';
      };
    }

    if (this._styles._max === 0) {
      Utils.insertCSSRule(this._styles, prefix, 'min-height: ' + getHeight(1, 0) + ';', 0);
    }

    if (maxHeight > this._styles._max) {
      for (var i = this._styles._max; i < maxHeight; ++i) {
        Utils.insertCSSRule(this._styles,
          prefix + '[data-gs-height="' + (i + 1) + '"]',
          'height: ' + getHeight(i + 1, i) + ';',
          i
        );
        Utils.insertCSSRule(this._styles,
          prefix + '[data-gs-min-height="' + (i + 1) + '"]',
          'min-height: ' + getHeight(i + 1, i) + ';',
          i
        );
        Utils.insertCSSRule(this._styles,
          prefix + '[data-gs-max-height="' + (i + 1) + '"]',
          'max-height: ' + getHeight(i + 1, i) + ';',
          i
        );
        Utils.insertCSSRule(this._styles,
          prefix + '[data-gs-y="' + i + '"]',
          'top: ' + getHeight(i, i) + ';',
          i
        );
      }
      this._styles._max = maxHeight;
    }
  };

  GridStack.prototype._updateContainerHeight = function() {
    if (this.engine._batchMode) { return; }
    var row = this.engine.getRow();
    if (row < this.opts.minRow) {
      row = this.opts.minRow;
    }
    // check for css min height. Each row is cellHeight + verticalMargin, until last one which has no margin below
    var cssMinHeight = parseInt(this.$el.css('min-height'));
    if (cssMinHeight > 0) {
      var verticalMargin = this.opts.verticalMargin;
      var minRow =  Math.round((cssMinHeight + verticalMargin) / (this.cellHeight() + verticalMargin));
      if (row < minRow) {
        row = minRow;
      }
    }
    this.$el.attr('data-gs-current-row', row);
    if (!this.opts.cellHeight) {
      return ;
    }
    if (!this.opts.verticalMargin) {
      this.$el.css('height', (row * (this.opts.cellHeight)) + this.opts.cellHeightUnit);
    } else if (this.opts.cellHeightUnit === this.opts.verticalMarginUnit) {
      this.$el.css('height', (row * (this.opts.cellHeight + this.opts.verticalMargin) -
        this.opts.verticalMargin) + this.opts.cellHeightUnit);
    } else {
      this.$el.css('height', 'calc(' + ((row * (this.opts.cellHeight)) + this.opts.cellHeightUnit) +
        ' + ' + ((row * (this.opts.verticalMargin - 1)) + this.opts.verticalMarginUnit) + ')');
    }
  };

  GridStack.prototype._setupRemovingTimeout = function(el) {
    var self = this;
    var node = $(el).data('_gridstack_node');

    if (node._removeTimeout || !self.opts.removable) {
      return;
    }
    node._removeTimeout = setTimeout(function() {
      el.addClass('grid-stack-item-removing');
      node._isAboutToRemove = true;
    }, self.opts.removeTimeout);
  };

  GridStack.prototype._clearRemovingTimeout = function(el) {
    var node = $(el).data('_gridstack_node');

    if (!node._removeTimeout) {
      return;
    }
    clearTimeout(node._removeTimeout);
    node._removeTimeout = null;
    el.removeClass('grid-stack-item-removing');
    node._isAboutToRemove = false;
  };

  GridStack.prototype._prepareElementsByNode = function(el, node) {
    var self = this;

    var cellWidth;
    var cellFullHeight; // internal cellHeight + v-margin

    var dragOrResize = function(event, ui) {
      var x = Math.round(ui.position.left / cellWidth);
      var y = Math.floor((ui.position.top + cellFullHeight / 2) / cellFullHeight);
      var width;
      var height;

      if (event.type === 'drag') {
        var distance = ui.position.top - node._prevYPix;
        node._prevYPix = ui.position.top;
        Utils.updateScrollPosition(el[0], ui, distance);
        if (el.data('inTrashZone') || x < 0 || x >= self.engine.column || y < 0 ||
          (!self.engine.float && y > self.engine.getRow())) {
          if (!node._temporaryRemoved) {
            if (self.opts.removable === true) {
              self._setupRemovingTimeout(el);
            }

            x = node._beforeDragX;
            y = node._beforeDragY;

            self.placeholder.detach();
            self.placeholder.hide();
            self.engine.removeNode(node);
            self._updateContainerHeight();

            node._temporaryRemoved = true;
          } else {
            return;
          }
        } else {
          self._clearRemovingTimeout(el);

          if (node._temporaryRemoved) {
            self.engine.addNode(node);
            self.placeholder
              .attr('data-gs-x', x)
              .attr('data-gs-y', y)
              .attr('data-gs-width', width)
              .attr('data-gs-height', height)
              .show();
            self.$el.append(self.placeholder);
            node.el = self.placeholder.get(0);
            node._temporaryRemoved = false;
          }
        }
      } else if (event.type === 'resize')  {
        if (x < 0) return;
        width = Math.round(ui.size.width / cellWidth);
        height = Math.round((ui.size.height + self.verticalMargin()) / cellFullHeight);
      }
      // width and height are undefined if not resizing
      var lastTriedWidth = width !== undefined ? width : node.lastTriedWidth;
      var lastTriedHeight = height !== undefined ? height : node.lastTriedHeight;
      if (!self.engine.canMoveNode(node, x, y, width, height) ||
        (node.lastTriedX === x && node.lastTriedY === y &&
        node.lastTriedWidth === lastTriedWidth && node.lastTriedHeight === lastTriedHeight)) {
        return;
      }
      node.lastTriedX = x;
      node.lastTriedY = y;
      node.lastTriedWidth = width;
      node.lastTriedHeight = height;
      self.engine.moveNode(node, x, y, width, height);
      self._updateContainerHeight();

      if (event.type === 'resize')  {
        $(event.target).trigger('gsresize', node);
      }
    };

    var onStartMoving = function(event, ui) {
      self.$el.append(self.placeholder);
      var o = $(this);
      self.engine.cleanNodes();
      self.engine.beginUpdate(node);
      cellWidth = self.cellWidth();
      var strictCellHeight = self.cellHeight(); // heigh without v-margin
      // compute height with v-margin (Note: we add 1 margin as last row is missing it)
      cellFullHeight = (self.$el.height() + self.verticalMargin()) / parseInt(self.$el.attr('data-gs-current-row'));
      self.placeholder
        .attr('data-gs-x', o.attr('data-gs-x'))
        .attr('data-gs-y', o.attr('data-gs-y'))
        .attr('data-gs-width', o.attr('data-gs-width'))
        .attr('data-gs-height', o.attr('data-gs-height'))
        .show();
      node.el = self.placeholder.get(0);
      node._beforeDragX = node.x;
      node._beforeDragY = node.y;
      node._prevYPix = ui.position.top;
      var minHeight = (node.minHeight || 1);
      var verticalMargin = self.opts.verticalMargin;

      // mineHeight - Each row is cellHeight + verticalMargin, until last one which has no margin below
      self.dd.resizable(el, 'option', 'minWidth', cellWidth * (node.minWidth || 1));
      self.dd.resizable(el, 'option', 'minHeight', (strictCellHeight * minHeight) + (minHeight - 1) * verticalMargin);

      if (event.type === 'resizestart') {
        o.find('.grid-stack-item').trigger('resizestart');
      }
    };

    var onEndMoving = function(event, ui) {
      var o = $(this);
      if (!o.data('_gridstack_node')) {
        return;
      }

      // var forceNotify = false; what is the point of calling 'change' event with no data, when the 'removed' event is already called ?
      self.placeholder.detach();
      node.el = o.get(0);
      self.placeholder.hide();

      if (node._isAboutToRemove) {
        // forceNotify = true;
        var gridToNotify = el.data('_gridstack_node')._grid;
        gridToNotify._triggerRemoveEvent();
        el.removeData('_gridstack_node');
        el.remove();
      } else {
        self._clearRemovingTimeout(el);
        if (!node._temporaryRemoved) {
          Utils.removePositioningStyles(o);
          o
            .attr('data-gs-x', node.x)
            .attr('data-gs-y', node.y)
            .attr('data-gs-width', node.width)
            .attr('data-gs-height', node.height);
        } else {
          Utils.removePositioningStyles(o);
          o
            .attr('data-gs-x', node._beforeDragX)
            .attr('data-gs-y', node._beforeDragY)
            .attr('data-gs-width', node.width)
            .attr('data-gs-height', node.height);
          node.x = node._beforeDragX;
          node.y = node._beforeDragY;
          node._temporaryRemoved = false;
          self.engine.addNode(node);
        }
      }
      self._updateContainerHeight();
      self._triggerChangeEvent(/*forceNotify*/);

      self.engine.endUpdate();

      var nestedGrids = o.find('.grid-stack');
      if (nestedGrids.length && event.type === 'resizestop') {
        nestedGrids.each(function(index, el) {
          el.gridstack.onResizeHandler();
        });
        o.find('.grid-stack-item').trigger('resizestop');
        o.find('.grid-stack-item').trigger('gsresizestop');
      }
      if (event.type === 'resizestop') {
        self.$el.trigger('gsresizestop', o);
      }
    };

    this.dd
      .draggable(el, {
        start: onStartMoving,
        stop: onEndMoving,
        drag: dragOrResize
      })
      .resizable(el, {
        start: onStartMoving,
        stop: onEndMoving,
        resize: dragOrResize
      });

    if (node.noMove || this.opts.disableDrag || this.opts.staticGrid) {
      this.dd.draggable(el, 'disable');
    }

    if (node.noResize || this.opts.disableResize || this.opts.staticGrid) {
      this.dd.resizable(el, 'disable');
    }

    this._writeAttr(el, node);
  };

  GridStack.prototype._prepareElement = function(el, triggerAddEvent) {
    triggerAddEvent = triggerAddEvent !== undefined ? triggerAddEvent : false;
    var self = this;
    el = $(el);

    el.addClass(this.opts.itemClass);
    var node = this._readAttr(el, {el: el.get(0), _grid: self});
    node = self.engine.addNode(node, triggerAddEvent);
    el.data('_gridstack_node', node);

    this._prepareElementsByNode(el, node);
  };

  /** call to write any default attributes back to element */
  GridStack.prototype._writeAttr = function(el, node) {
    if (!node) { return; }
    el = $(el);
    // Note: passing null removes the attr in jquery
    if (node.x !== undefined) { el.attr('data-gs-x', node.x); }
    if (node.y !== undefined) { el.attr('data-gs-y', node.y); }
    if (node.width !== undefined) { el.attr('data-gs-width', node.width); }
    if (node.height !== undefined) { el.attr('data-gs-height', node.height); }
    if (node.autoPosition !== undefined) { el.attr('data-gs-auto-position', node.autoPosition ? true : null); }
    if (node.minWidth !== undefined) { el.attr('data-gs-min-width', node.minWidth); }
    if (node.maxWidth !== undefined) { el.attr('data-gs-max-width', node.maxWidth); }
    if (node.minHeight !== undefined) { el.attr('data-gs-min-height', node.minHeight); }
    if (node.maxHeight !== undefined) { el.attr('data-gs-max-height', node.maxHeight); }
    if (node.noResize !== undefined) { el.attr('data-gs-no-resize', node.noResize ? true : null); }
    if (node.noMove !== undefined) { el.attr('data-gs-no-move', node.noMove ? true : null); }
    if (node.locked !== undefined) { el.attr('data-gs-locked', node.locked ? true : null); }
    if (node.resizeHandles !== undefined) { el.attr('data-gs-resize-handles', node.resizeHandles); }
    if (node.id !== undefined) { el.attr('data-gs-id', node.id); }
  };

  /** call to read any default attributes back to element */
  GridStack.prototype._readAttr = function(el, node) {
    el = $(el);
    node = node || {};
    node.x = el.attr('data-gs-x');
    node.y = el.attr('data-gs-y');
    node.width = el.attr('data-gs-width');
    node.height = el.attr('data-gs-height');
    node.autoPosition = Utils.toBool(el.attr('data-gs-auto-position'));
    node.maxWidth = el.attr('data-gs-max-width');
    node.minWidth = el.attr('data-gs-min-width');
    node.maxHeight = el.attr('data-gs-max-height');
    node.minHeight = el.attr('data-gs-min-height');
    node.noResize = Utils.toBool(el.attr('data-gs-no-resize'));
    node.noMove = Utils.toBool(el.attr('data-gs-no-move'));
    node.locked = Utils.toBool(el.attr('data-gs-locked'));
    node.resizeHandles = el.attr('data-gs-resize-handles');
    node.id = el.attr('data-gs-id');
    return node;
  };

  GridStack.prototype.setAnimation = function(enable) {
    if (enable) {
      this.$el.addClass('grid-stack-animate');
    } else {
      this.$el.removeClass('grid-stack-animate');
    }
  };

  GridStack.prototype.addWidget = function(el, opt, y, width, height, autoPosition, minWidth, maxWidth, minHeight, maxHeight, id) {

    // new way of calling with an object - make sure all items have been properly initialized
    if (opt === undefined || typeof opt === 'object') {
      // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos
      // as the actual value are filled in when _prepareElement() calls el.attr('data-gs-xyz) before adding the node.
      // opt = this.engine._prepareNode(opt);
    } else {
      // old legacy way of calling with items spelled out - call us back with single object instead (so we can properly initialized values)
      return this.addWidget(el, {x: opt, y: y, width: width, height: height, autoPosition: autoPosition,
        minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, id: id});
    }

    el = $(el);
    if (opt) { // see knockout above
      // make sure we load any DOM attributes that are not specified in passed in options (which override)
      domAttr = this._readAttr(el);
      Utils.defaults(opt, domAttr);
      this.engine._prepareNode(opt);
    }
    this._writeAttr(el, opt);
    this.$el.append(el);
    return this.makeWidget(el);
  };

  GridStack.prototype.makeWidget = function(el) {
    el = $(el);
    this._prepareElement(el, true);
    this._updateContainerHeight();
    this._triggerAddEvent();
    this._triggerChangeEvent(true); // trigger any other changes

    return el.get(0);
  };

  GridStack.prototype.willItFit = function(x, y, width, height, autoPosition) {
    var node = {x: x, y: y, width: width, height: height, autoPosition: autoPosition};
    return this.engine.canBePlacedWithRespectToHeight(node);
  };

  GridStack.prototype.removeWidget = function(el, detachNode) {
    el = $(el);
    var node = el.data('_gridstack_node');
    // For Meteor support: https://github.com/gridstack/gridstack.js/pull/272
    if (!node) {
      node = this.engine.getNodeDataByDOMEl(el.get(0));
    }
    if (!node || node.el.parentElement !== this.el) return; // not our child!
    // remove our DOM data (circular link) and drag&drop permanently
    el.removeData('_gridstack_node');
    this.dd.draggable(el, 'destroy').resizable(el, 'destroy');

    this.engine.removeNode(node, detachNode);
    this._triggerRemoveEvent();
    this._triggerChangeEvent(true); // trigger any other changes
  };

  GridStack.prototype.removeAll = function(detachNode) {
    // always remove our DOM data (circular link) before list gets emptied and drag&drop permanently
    this.engine.nodes.forEach(function(node) {
      var el = $(node.el);
      el.removeData('_gridstack_node');
      this.dd.draggable(el, 'destroy').resizable(el, 'destroy');
    }, this);

    this.engine.removeAll(detachNode);
    this._triggerRemoveEvent();
  };

  GridStack.prototype.destroy = function(detachGrid) {
    $(window).off('resize', this.onResizeHandler);
    if (detachGrid === false) {
      this.removeAll(false);
      this.$el.removeClass(this.opts._class);
      delete this.$el.get(0).gridstack;
    } else {
      this.$el.remove();
    }
    Utils.removeStylesheet(this._stylesId);
    if (this.engine) {
      this.engine = null;
    }
  };

  GridStack.prototype.resizable = function(el, val) {
    var self = this;
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      node.noResize = !(val || false);
      if (node.noResize) {
        self.dd.resizable(el, 'disable');
      } else {
        self.dd.resizable(el, 'enable');
      }
    });
    return this;
  };

  GridStack.prototype.movable = function(el, val) {
    var self = this;
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      node.noMove = !(val || false);
      if (node.noMove) {
        self.dd.draggable(el, 'disable');
        el.removeClass('ui-draggable-handle');
      } else {
        self.dd.draggable(el, 'enable');
        el.addClass('ui-draggable-handle');
      }
    });
    return this;
  };

  GridStack.prototype.enableMove = function(doEnable, includeNewWidgets) {
    this.movable(this.$el.children('.' + this.opts.itemClass), doEnable);
    if (includeNewWidgets) {
      this.opts.disableDrag = !doEnable;
    }
  };

  GridStack.prototype.enableResize = function(doEnable, includeNewWidgets) {
    this.resizable(this.$el.children('.' + this.opts.itemClass), doEnable);
    if (includeNewWidgets) {
      this.opts.disableResize = !doEnable;
    }
  };

  GridStack.prototype.disable = function() {
    this.movable(this.$el.children('.' + this.opts.itemClass), false);
    this.resizable(this.$el.children('.' + this.opts.itemClass), false);
    this.$el.trigger('disable');
  };

  GridStack.prototype.enable = function() {
    this.movable(this.$el.children('.' + this.opts.itemClass), true);
    this.resizable(this.$el.children('.' + this.opts.itemClass), true);
    this.$el.trigger('enable');
  };

  GridStack.prototype.locked = function(el, val) {
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      node.locked = (val || false);
      el.attr('data-gs-locked', node.locked ? 'yes' : null);
    });
    return this;
  };

  GridStack.prototype.maxHeight = function(el, val) {
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      if (!isNaN(val)) {
        node.maxHeight = (val || false);
        el.attr('data-gs-max-height', val);
      }
    });
    return this;
  };

  GridStack.prototype.minHeight = function(el, val) {
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      if (!isNaN(val)) {
        node.minHeight = (val || false);
        el.attr('data-gs-min-height', val);
      }
    });
    return this;
  };

  GridStack.prototype.maxWidth = function(el, val) {
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      if (!isNaN(val)) {
        node.maxWidth = (val || false);
        el.attr('data-gs-max-width', val);
      }
    });
    return this;
  };

  GridStack.prototype.minWidth = function(el, val) {
    el = $(el);
    el.each(function(index, el) {
      el = $(el);
      var node = el.data('_gridstack_node');
      if (!node) { return; }
      if (!isNaN(val)) {
        node.minWidth = (val || false);
        el.attr('data-gs-min-width', val);
      }
    });
    return this;
  };

  GridStack.prototype._updateElement = function(el, callback) {
    el = $(el).first();
    var node = el.data('_gridstack_node');
    if (!node) { return; }
    var self = this;

    self.engine.cleanNodes();
    self.engine.beginUpdate(node);

    callback.call(this, el, node);

    self._updateContainerHeight();
    self._triggerChangeEvent();

    self.engine.endUpdate();
  };

  GridStack.prototype.resize = function(el, width, height) {
    this._updateElement(el, function(el, node) {
      width = (width !== null && width !== undefined) ? width : node.width;
      height = (height !== null && height !== undefined) ? height : node.height;

      this.engine.moveNode(node, node.x, node.y, width, height);
    });
  };

  GridStack.prototype.move = function(el, x, y) {
    this._updateElement(el, function(el, node) {
      x = (x !== null && x !== undefined) ? x : node.x;
      y = (y !== null && y !== undefined) ? y : node.y;

      this.engine.moveNode(node, x, y, node.width, node.height);
    });
  };

  GridStack.prototype.update = function(el, x, y, width, height) {
    this._updateElement(el, function(el, node) {
      x = (x !== null && x !== undefined) ? x : node.x;
      y = (y !== null && y !== undefined) ? y : node.y;
      width = (width !== null && width !== undefined) ? width : node.width;
      height = (height !== null && height !== undefined) ? height : node.height;

      this.engine.moveNode(node, x, y, width, height);
    });
  };

  /**
   * relayout grid items to reclaim any empty space
   */
  GridStack.prototype.compact = function() {
    if (this.engine.nodes.length === 0) { return; }
    this.batchUpdate();
    this.engine._sortNodes();
    var nodes = this.engine.nodes;
    this.engine.nodes = []; // pretend we have no nodes to conflict layout to start with...
    nodes.forEach(function(node) {
      if (!node.noMove && !node.locked) {
        node.autoPosition = true;
      }
      this.engine.addNode(node, false); // 'false' for add event trigger
      node._dirty = true; // force attr update
    }, this);
    this.commit();
  };

  GridStack.prototype.verticalMargin = function(val, noUpdate) {
    if (val === undefined) {
      return this.opts.verticalMargin;
    }

    var heightData = Utils.parseHeight(val);

    if (this.opts.verticalMarginUnit === heightData.unit && this.opts.maxRow === heightData.height) {
      return ;
    }
    this.opts.verticalMarginUnit = heightData.unit;
    this.opts.verticalMargin = heightData.height;

    if (!noUpdate) {
      this._updateStyles();
    }
  };

  /** set/get the current cell height value */
  GridStack.prototype.cellHeight = function(val, noUpdate) {
    // getter - returns the opts stored height else compute it...
    if (val === undefined) {
      if (this.opts.cellHeight && this.opts.cellHeight !== 'auto') {
        return this.opts.cellHeight;
      }
      // compute the height taking margin into account (each row has margin other than last one)
      var o = this.$el.children('.' + this.opts.itemClass).first();
      var height = o.attr('data-gs-height');
      var verticalMargin = this.opts.verticalMargin;
      return Math.round((o.outerHeight() - (height - 1) * verticalMargin) / height);
    }

    // setter - updates the cellHeight value if they changed
    var heightData = Utils.parseHeight(val);
    if (this.opts.cellHeightUnit === heightData.unit && this.opts.cellHeight === heightData.height) {
      return ;
    }
    this.opts.cellHeightUnit = heightData.unit;
    this.opts.cellHeight = heightData.height;

    if (!noUpdate) {
      this._updateStyles();
    }
  };

  GridStack.prototype.cellWidth = function() {
    // TODO: take margin into account ($horizontal_padding in .scss) to make cellHeight='auto' square ? (see 810-many-columns.html)
    return Math.round(this.$el.outerWidth() / this.opts.column);
  };

  GridStack.prototype.getCellFromPixel = function(position, useOffset) {
    var containerPos = (useOffset !== undefined && useOffset) ?
      this.$el.offset() : this.$el.position();
    var relativeLeft = position.left - containerPos.left;
    var relativeTop = position.top - containerPos.top;

    var columnWidth = Math.floor(this.$el.width() / this.opts.column);
    var rowHeight = Math.floor(this.$el.height() / parseInt(this.$el.attr('data-gs-current-row')));

    return {x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight)};
  };

  GridStack.prototype.batchUpdate = function() {
    this.engine.batchUpdate();
  };

  GridStack.prototype.commit = function() {
    this.engine.commit();
    this._triggerRemoveEvent();
    this._triggerAddEvent();
    this._triggerChangeEvent();
  };

  GridStack.prototype.isAreaEmpty = function(x, y, width, height) {
    return this.engine.isAreaEmpty(x, y, width, height);
  };

  GridStack.prototype.setStatic = function(staticValue) {
    this.opts.staticGrid = (staticValue === true);
    this.enableMove(!staticValue);
    this.enableResize(!staticValue);
    this._setStaticClass();
  };

  GridStack.prototype._setStaticClass = function() {
    var staticClassName = 'grid-stack-static';

    if (this.opts.staticGrid === true) {
      this.$el.addClass(staticClassName);
    } else {
      this.$el.removeClass(staticClassName);
    }
  };

  /** called whenever a node is added or moved - updates the cached layouts */
  GridStackEngine.prototype._layoutsNodesChange = function(nodes) {
    if (!this._layouts || this._ignoreLayoutsNodeChange) return;
    // remove smaller layouts - we will re-generate those on the fly... larger ones need to update
    this._layouts.forEach(function(layout, column) {
      if (!layout || column === this.column) return;
      if (column < this.column) {
        this._layouts[column] = undefined;
      }
      else {
        // we save the original x,y,w (h isn't cached) to see what actually changed to propagate better.
        // Note: we don't need to check against out of bound scaling/moving as that will be done when using those cache values.
        nodes.forEach(function(node) {
          var n = layout.find(function(l) { return l._id === node._id });
          if (!n) return; // no cache for new nodes. Will use those values.
          var ratio = column / this.column;
          // Y changed, push down same amount
          // TODO: detect doing item 'swaps' will help instead of move (especially in 1 column mode)
          if (node.y !== node._origY) {
            n.y += (node.y - node._origY);
          }
          // X changed, scale from new position
          if (node.x !== node._origX) {
            n.x = Math.round(node.x * ratio);
          }
          // width changed, scale from new width
          if (node.width !== node._origW) {
            n.width = Math.round(node.width * ratio);
          }
          // ...height always carries over from cache
        }, this);
      }
    }, this);
  }

  /**
   * Called to scale the widget width & position up/down based on the column change.
   * Note we store previous layouts (especially original ones) to make it possible to go
   * from say 12 -> 1 -> 12 and get back to where we were.
   *
   * oldColumn: previous number of columns
   * column:    new column number
   * nodes?:    different sorted list (ex: DOM order) instead of current list
   */
  GridStackEngine.prototype._updateNodeWidths = function(oldColumn, column, nodes) {
    if (!this.nodes.length || oldColumn === column) { return; }

    // cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data
    var copy = [this.nodes.length];
    this.nodes.forEach(function(n, i) {copy[i] = {x: n.x, y: n.y, width: n.width, _id: n._id}}); // only thing we change is x,y,w and id to find it back
    this._layouts = this._layouts || []; // use array to find larger quick
    this._layouts[oldColumn] = copy;

    // if we're going to 1 column and using DOM order rather than default sorting, then generate that layout
    if (column === 1 && nodes && nodes.length) {
      var top = 0;
      nodes.forEach(function(n) {
        n.x = 0;
        n.width = 1;
        n.y = Math.max(n.y, top);
        top = n.y + n.height;
      });
    } else {
      nodes = Utils.sort(this.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision)
    }

    // see if we have cached previous layout.
    var cacheNodes = this._layouts[column] || [];
    // if not AND we are going up in size start with the largest layout as down-scaling is more accurate
    var lastIndex = this._layouts.length - 1;
    if (cacheNodes.length === 0 && column > oldColumn && column < lastIndex) {
      cacheNodes = this._layouts[lastIndex] || [];
      if (cacheNodes.length) {
        // pretend we came from that larger column by assigning those values as starting point
        oldColumn = lastIndex;
        cacheNodes.forEach(function(cacheNode) {
          var j = nodes.findIndex(function(n) {return n && n._id === cacheNode._id});
          if (j !== -1) {
            // still current, use cache info positions
            nodes[j].x = cacheNode.x;
            nodes[j].y = cacheNode.y;
            nodes[j].width = cacheNode.width;
          }
        });
        cacheNodes = []; // we still don't have new column cached data... will generate from larger one.
      }
    }

    // if we found cache re-use those nodes that are still current
    var newNodes = [];
    cacheNodes.forEach(function(cacheNode) {
      var j = nodes.findIndex(function(n) {return n && n._id === cacheNode._id});
      if (j !== -1) {
        // still current, use cache info positions
        nodes[j].x = cacheNode.x;
        nodes[j].y = cacheNode.y;
        nodes[j].width = cacheNode.width;
        newNodes.push(nodes[j]);
        nodes[j] = null; // erase it so we know what's left
      }
    });
    // ...and add any extra non-cached ones
    var ratio = column / oldColumn;
    nodes.forEach(function(node) {
      if (!node) return;
      node.x = (column === 1 ? 0 : Math.round(node.x * ratio));
      node.width = ((column === 1 || oldColumn === 1) ? 1 : (Math.round(node.width * ratio) || 1));
      newNodes.push(node);
    });

    // finally relayout them in reverse order (to get correct placement)
    newNodes = Utils.sort(newNodes, -1, column);
    this._ignoreLayoutsNodeChange = true;
    this.batchUpdate();
    this.nodes = []; // pretend we have no nodes to start with (we use same structures) to simplify layout
    newNodes.forEach(function(node) {
      this.addNode(node, false); // 'false' for add event trigger
      node._dirty = true; // force attr update
    }, this);
    this.commit();
    delete this._ignoreLayoutsNodeChange;
  }

  /** called to save initial position/size */
  GridStackEngine.prototype._saveInitial = function() {
    this.nodes.forEach(function(n) {
      n._origX = n.x;
      n._origY = n.y;
      n._origW = n.width;
      n._origH = n.height;
      delete n._dirty;
    });
  }

  /**
   * set/get number of columns in the grid. Will attempt to update existing widgets
   * to conform to new number of columns. Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11],
   * else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns)
   * @param column - Integer > 0 (default 12).
   * @param doNotPropagate if true existing widgets will not be updated (optional)
   */
  GridStack.prototype.column = function(column, doNotPropagate) {
    // getter - returns the opts stored mode
    if (column === undefined) {
      return this.opts.column;
    }
    // setter
    if (this.opts.column === column) { return; }
    var oldColumn = this.opts.column;

    // if we go into 1 column mode (which happens if we're sized less than minWidth unless disableOneColumnMode is on)
    // then remember the original columns so we can restore.
    if (column === 1) {
      this._prevColumn = oldColumn;
    } else {
      delete this._prevColumn;
    }

    this.$el.removeClass('grid-stack-' + oldColumn);
    this.$el.addClass('grid-stack-' + column);
    this.opts.column = this.engine.column = column;

    if (doNotPropagate === true) { return; }

    // update the items now - see if the dom order nodes should be passed instead (else default to current list)
    var domNodes;
    if (this.opts.oneColumnModeDomSort && column === 1) {
      domNodes = [];
      this.$el.children('.' + this.opts.itemClass).each(function(index, el) {
        var node = $(el).data('_gridstack_node');
        if (node) { domNodes.push(node); }
      });
      if (!domNodes.length) { domNodes = undefined; }
    }
    this.engine._updateNodeWidths(oldColumn, column, domNodes);

    // and trigger our event last...
    this.engine._ignoreLayoutsNodeChange = true;
    this._triggerChangeEvent();
    delete this.engine._ignoreLayoutsNodeChange;
  };

  GridStack.prototype.float = function(val) {
    // getter - returns the opts stored mode
    if (val === undefined) {
      return this.opts.float || false;
    }
    // setter - updates the mode and relayout if gravity is back on
    if (this.opts.float === val) { return; }
    this.opts.float = this.engine.float = val || false;
    if (!val) {
      this.engine._packNodes();
      this.engine._notify();
      this._triggerChangeEvent();
    }
  };

  GridStack.prototype.getRow = function() {
    return this.engine.getRow();
  }

  /** Event handler that extracts our CustomEvent data out automatically for receiving custom
   * notifications (see doc for supported events)
   */
  GridStack.prototype.on = function(eventName, callback) {
    // check for array of names being passed instead
    if (eventName.indexOf(' ') !== -1) {
      var names = eventName.split(' ');
      names.forEach(function(name) { this.on(name, callback) }, this);
      return;
    }

    if (eventName === 'change' || eventName === 'added' || eventName === 'removed') {
      // native CustomEvent handlers - cash the generic handlers so we can remove
      this._gsEventHandler = this._gsEventHandler || {};
      this._gsEventHandler[eventName] = function(event) { callback(event, event.detail) };
      this.el.addEventListener(eventName, this._gsEventHandler[eventName]);
    } else {
      // still JQuery events
      this.$el.on(eventName, callback);
    }
  }

  /** unsubscribe from the 'on' event */
  GridStack.prototype.off = function(eventName) {
    // check for array of names being passed instead
    if (eventName.indexOf(' ') !== -1) {
      var names = eventName.split(' ');
      names.forEach(function(name) { this.off(name, callback) }, this);
      return;
    }

    if (eventName === 'change' || eventName === 'added' || eventName === 'removed') {
      // remove native CustomEvent handlers
      if (this._gsEventHandler && this._gsEventHandler[eventName]) {
        this.el.removeEventListener(eventName, this._gsEventHandler[eventName]);
        delete this._gsEventHandler[eventName];
      }
    } else {
      // still JQuery events
      this.$el.off(eventName);
    }
  }

  // legacy method renames
  GridStack.prototype.setGridWidth = obsolete(GridStack.prototype.column, 'setGridWidth', 'column', 'v0.5.3');
  GridStack.prototype.setColumn = obsolete(GridStack.prototype.column, 'setColumn', 'column', 'v0.6.4');
  GridStackEngine.prototype.getGridHeight = obsolete(GridStackEngine.prototype.getRow, 'getGridHeight', 'getRow', 'v1.0.0');

  scope.GridStack = GridStack;
  scope.GridStack.Utils = Utils;
  scope.GridStack.Engine = GridStackEngine;
  scope.GridStack.DragDropPlugin = GridStackDragDropPlugin;

  /**
   * initializing the HTML element, or selector string, into a grid will return the grid. Calling it again will
   * simply return the existing instance (ignore any passed options).
   */
  GridStack.init = function(opts, elOrString) {
    if (!elOrString) { elOrString = '.grid-stack' }
    var el = $(elOrString).get(0);
    if (!el) return;
    if (!el.gridstack) {
      el.gridstack = new GridStack(el, Utils.clone(opts));
    }
    return el.gridstack
  };

  /**
   * Will initialize a list of elements (given a selector) and return an array of grids.
   */
  GridStack.initAll = function(opts, selector) {
    if (!selector) { selector = '.grid-stack' }
    var grids = [];
    $(selector).each(function(index, el) {
      if (!el.gridstack) {
        el.gridstack = new GridStack(el, Utils.clone(opts));
      }
      grids.push(el.gridstack);
    });
    return grids;
  };

  return scope.GridStack;
});
;
/** gridstack.js 1.1.2 - JQuery UI Drag&Drop plugin @preserve */
/**
 * https://gridstackjs.com/
 * (c) 2014-2020 Alain Dumesny, Dylan Weiss, Pavel Reznikov
 * gridstack.js may be freely distributed under the MIT license.
*/
(function(factory) {
  /* we compile this in so no need for required loading
  if (typeof define === 'function' && define.amd) {
    define(['jquery', 'gridstack', 'exports'], factory);
  } else if (typeof exports !== 'undefined') {
    try { jQuery = require('jquery'); } catch (e) {}
    try { gridstack = require('gridstack'); } catch (e) {}
    factory(jQuery, gridstack.GridStack, exports);
  } else */{
    factory(jQuery, GridStack, window);
  }
})(function($, GridStack, scope) {
  /**
  * @class JQueryUIGridStackDragDropPlugin
  * jQuery UI implementation of drag'n'drop gridstack plugin.
  */
  function JQueryUIGridStackDragDropPlugin(grid) {
    GridStack.DragDropPlugin.call(this, grid);
  }

  GridStack.DragDropPlugin.registerPlugin(JQueryUIGridStackDragDropPlugin);

  JQueryUIGridStackDragDropPlugin.prototype = Object.create(GridStack.DragDropPlugin.prototype);
  JQueryUIGridStackDragDropPlugin.prototype.constructor = JQueryUIGridStackDragDropPlugin;

  JQueryUIGridStackDragDropPlugin.prototype.resizable = function(el, opts) {
    el = $(el);
    if (opts === 'disable' || opts === 'enable' || opts === 'destroy') {
      el.resizable(opts);
    } else if (opts === 'option') {
      var key = arguments[2];
      var value = arguments[3];
      el.resizable(opts, key, value);
    } else {
      var handles = el.data('gs-resize-handles') ? el.data('gs-resize-handles') :
        this.grid.opts.resizable.handles;
      el.resizable($.extend({}, this.grid.opts.resizable, {
        handles: handles
      }, {
        start: opts.start || function() {},
        stop: opts.stop || function() {},
        resize: opts.resize || function() {}
      }));
    }
    return this;
  };

  JQueryUIGridStackDragDropPlugin.prototype.draggable = function(el, opts) {
    el = $(el);
    if (opts === 'disable' || opts === 'enable' || opts === 'destroy') {
      el.draggable(opts);
    } else {
      el.draggable($.extend({}, this.grid.opts.draggable, {
        containment: (this.grid.opts.isNested && !this.grid.opts.dragOut) ?
          this.grid.$el.parent() :
          (this.grid.opts.draggable.containment || null),
        start: opts.start || function() {},
        stop: opts.stop || function() {},
        drag: opts.drag || function() {}
      }));
    }
    return this;
  };

  JQueryUIGridStackDragDropPlugin.prototype.droppable = function(el, opts) {
    el = $(el);
    el.droppable(opts);
    return this;
  };

  JQueryUIGridStackDragDropPlugin.prototype.isDroppable = function(el, opts) {
    el = $(el);
    return Boolean(el.data('droppable'));
  };

  JQueryUIGridStackDragDropPlugin.prototype.on = function(el, eventName, callback) {
    $(el).on(eventName, callback);
    return this;
  };

  scope.JQueryUIGridStackDragDropPlugin = JQueryUIGridStackDragDropPlugin;

  return JQueryUIGridStackDragDropPlugin;
});
;
/**
 * jquery.mask.js
 * @version: v1.14.16
 * @author: Igor Escobar
 *
 * Created by Igor Escobar on 2012-03-10. Please report any bug at github.com/igorescobar/jQuery-Mask-Plugin
 *
 * Copyright (c) 2012 Igor Escobar http://igorescobar.com
 *
 * The MIT License (http://www.opensource.org/licenses/mit-license.php)
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

/* jshint laxbreak: true */
/* jshint maxcomplexity:17 */
/* global define */

// UMD (Universal Module Definition) patterns for JavaScript modules that work everywhere.
// https://github.com/umdjs/umd/blob/master/templates/jqueryPlugin.js
(function (factory, jQuery, Zepto) {

    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else if (typeof exports === 'object' && typeof Meteor === 'undefined') {
        module.exports = factory(require('jquery'));
    } else {
        factory(jQuery || Zepto);
    }

}(function ($) {
    'use strict';

    var Mask = function (el, mask, options) {

        var p = {
            invalid: [],
            getCaret: function () {
                try {
                    var sel,
                        pos = 0,
                        ctrl = el.get(0),
                        dSel = document.selection,
                        cSelStart = ctrl.selectionStart;

                    // IE Support
                    if (dSel && navigator.appVersion.indexOf('MSIE 10') === -1) {
                        sel = dSel.createRange();
                        sel.moveStart('character', -p.val().length);
                        pos = sel.text.length;
                    }
                    // Firefox support
                    else if (cSelStart || cSelStart === '0') {
                        pos = cSelStart;
                    }

                    return pos;
                } catch (e) { }
            },
            setCaret: function (pos) {
                try {
                    if (el.is(':focus')) {
                        var range, ctrl = el.get(0);

                        // Firefox, WebKit, etc..
                        if (ctrl.setSelectionRange) {
                            ctrl.setSelectionRange(pos, pos);
                        } else { // IE
                            range = ctrl.createTextRange();
                            range.collapse(true);
                            range.moveEnd('character', pos);
                            range.moveStart('character', pos);
                            range.select();
                        }
                    }
                } catch (e) { }
            },
            events: function () {
                el
                    .on('keydown.mask', function (e) {
                        el.data('mask-keycode', e.keyCode || e.which);
                        el.data('mask-previus-value', el.val());
                        el.data('mask-previus-caret-pos', p.getCaret());
                        p.maskDigitPosMapOld = p.maskDigitPosMap;
                    })
                    .on($.jMaskGlobals.useInput ? 'input.mask' : 'keyup.mask', p.behaviour)
                    .on('paste.mask drop.mask', function () {
                        setTimeout(function () {
                            el.keydown().keyup();
                        }, 100);
                    })
                    .on('change.mask', function () {
                        el.data('changed', true);
                    })
                    .on('blur.mask', function () {
                        if (oldValue !== p.val() && !el.data('changed')) {
                            el.trigger('change');
                        }
                        el.data('changed', false);
                    })
                    // it's very important that this callback remains in this position
                    // otherwhise oldValue it's going to work buggy
                    .on('blur.mask', function () {
                        oldValue = p.val();
                    })
                    // select all text on focus
                    .on('focus.mask', function (e) {
                        if (options.selectOnFocus === true) {
                            $(e.target).select();
                        }
                    })
                    // clear the value if it not complete the mask
                    .on('focusout.mask', function () {
                        if (options.clearIfNotMatch && !regexMask.test(p.val())) {
                            p.val('');
                        }
                    });
            },
            getRegexMask: function () {
                var maskChunks = [], translation, pattern, optional, recursive, oRecursive, r;

                for (var i = 0; i < mask.length; i++) {
                    translation = jMask.translation[mask.charAt(i)];

                    if (translation) {

                        pattern = translation.pattern.toString().replace(/.{1}$|^.{1}/g, '');
                        optional = translation.optional;
                        recursive = translation.recursive;

                        if (recursive) {
                            maskChunks.push(mask.charAt(i));
                            oRecursive = { digit: mask.charAt(i), pattern: pattern };
                        } else {
                            maskChunks.push(!optional && !recursive ? pattern : (pattern + '?'));
                        }

                    } else {
                        maskChunks.push(mask.charAt(i).replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
                    }
                }

                r = maskChunks.join('');

                if (oRecursive) {
                    r = r.replace(new RegExp('(' + oRecursive.digit + '(.*' + oRecursive.digit + ')?)'), '($1)?')
                        .replace(new RegExp(oRecursive.digit, 'g'), oRecursive.pattern);
                }

                return new RegExp(r);
            },
            destroyEvents: function () {
                el.off(['input', 'keydown', 'keyup', 'paste', 'drop', 'blur', 'focusout', ''].join('.mask '));
            },
            val: function (v) {
                var isInput = el.is('input'),
                    method = isInput ? 'val' : 'text',
                    r;

                if (arguments.length > 0) {
                    if (el[method]() !== v) {
                        el[method](v);
                    }
                    r = el;
                } else {
                    r = el[method]();
                }

                return r;
            },
            calculateCaretPosition: function (oldVal) {
                var newVal = p.getMasked(),
                    caretPosNew = p.getCaret();
                if (oldVal !== newVal) {
                    var caretPosOld = el.data('mask-previus-caret-pos') || 0,
                        newValL = newVal.length,
                        oldValL = oldVal.length,
                        maskDigitsBeforeCaret = 0,
                        maskDigitsAfterCaret = 0,
                        maskDigitsBeforeCaretAll = 0,
                        maskDigitsBeforeCaretAllOld = 0,
                        i = 0;

                    for (i = caretPosNew; i < newValL; i++) {
                        if (!p.maskDigitPosMap[i]) {
                            break;
                        }
                        maskDigitsAfterCaret++;
                    }

                    for (i = caretPosNew - 1; i >= 0; i--) {
                        if (!p.maskDigitPosMap[i]) {
                            break;
                        }
                        maskDigitsBeforeCaret++;
                    }

                    for (i = caretPosNew - 1; i >= 0; i--) {
                        if (p.maskDigitPosMap[i]) {
                            maskDigitsBeforeCaretAll++;
                        }
                    }

                    for (i = caretPosOld - 1; i >= 0; i--) {
                        if (p.maskDigitPosMapOld[i]) {
                            maskDigitsBeforeCaretAllOld++;
                        }
                    }

                    // if the cursor is at the end keep it there
                    if (caretPosNew > oldValL) {
                        caretPosNew = newValL * 10;
                    } else if (caretPosOld >= caretPosNew && caretPosOld !== oldValL) {
                        if (!p.maskDigitPosMapOld[caretPosNew]) {
                            var caretPos = caretPosNew;
                            caretPosNew -= maskDigitsBeforeCaretAllOld - maskDigitsBeforeCaretAll;
                            caretPosNew -= maskDigitsBeforeCaret;
                            if (p.maskDigitPosMap[caretPosNew]) {
                                caretPosNew = caretPos;
                            }
                        }
                    }
                    else if (caretPosNew > caretPosOld) {
                        caretPosNew += maskDigitsBeforeCaretAll - maskDigitsBeforeCaretAllOld;
                        caretPosNew += maskDigitsAfterCaret;
                    }
                }
                return caretPosNew;
            },
            behaviour: function (e) {
                e = e || window.event;
                p.invalid = [];

                var keyCode = el.data('mask-keycode');

                if ($.inArray(keyCode, jMask.byPassKeys) === -1) {
                    var newVal = p.getMasked(),
                        caretPos = p.getCaret(),
                        oldVal = el.data('mask-previus-value') || '';

                    // this is a compensation to devices/browsers that don't compensate
                    // caret positioning the right way
                    setTimeout(function () {
                        p.setCaret(p.calculateCaretPosition(oldVal));
                    }, $.jMaskGlobals.keyStrokeCompensation);

                    p.val(newVal);
                    p.setCaret(caretPos);
                    return p.callbacks(e);
                }
            },
            getMasked: function (skipMaskChars, val) {
                var buf = [],
                    value = val === undefined ? p.val() : val + '',
                    m = 0, maskLen = mask.length,
                    v = 0, valLen = value.length,
                    offset = 1, addMethod = 'push',
                    resetPos = -1,
                    maskDigitCount = 0,
                    maskDigitPosArr = [],
                    lastMaskChar,
                    check;

                if (options.reverse) {
                    addMethod = 'unshift';
                    offset = -1;
                    lastMaskChar = 0;
                    m = maskLen - 1;
                    v = valLen - 1;
                    check = function () {
                        return m > -1 && v > -1;
                    };
                } else {
                    lastMaskChar = maskLen - 1;
                    check = function () {
                        return m < maskLen && v < valLen;
                    };
                }

                var lastUntranslatedMaskChar;
                while (check()) {
                    var maskDigit = mask.charAt(m),
                        valDigit = value.charAt(v),
                        translation = jMask.translation[maskDigit];

                    if (translation) {
                        if (valDigit.match(translation.pattern)) {
                            buf[addMethod](valDigit);
                            if (translation.recursive) {
                                if (resetPos === -1) {
                                    resetPos = m;
                                } else if (m === lastMaskChar && m !== resetPos) {
                                    m = resetPos - offset;
                                }

                                if (lastMaskChar === resetPos) {
                                    m -= offset;
                                }
                            }
                            m += offset;
                        } else if (valDigit === lastUntranslatedMaskChar) {
                            // matched the last untranslated (raw) mask character that we encountered
                            // likely an insert offset the mask character from the last entry; fall
                            // through and only increment v
                            maskDigitCount--;
                            lastUntranslatedMaskChar = undefined;
                        } else if (translation.optional) {
                            m += offset;
                            v -= offset;
                        } else if (translation.fallback) {
                            buf[addMethod](translation.fallback);
                            m += offset;
                            v -= offset;
                        } else {
                            p.invalid.push({ p: v, v: valDigit, e: translation.pattern });
                        }
                        v += offset;
                    } else {
                        if (!skipMaskChars) {
                            buf[addMethod](maskDigit);
                        }

                        if (valDigit === maskDigit) {
                            maskDigitPosArr.push(v);
                            v += offset;
                        } else {
                            lastUntranslatedMaskChar = maskDigit;
                            maskDigitPosArr.push(v + maskDigitCount);
                            maskDigitCount++;
                        }

                        m += offset;
                    }
                }

                var lastMaskCharDigit = mask.charAt(lastMaskChar);
                if (maskLen === valLen + 1 && !jMask.translation[lastMaskCharDigit]) {
                    buf.push(lastMaskCharDigit);
                }

                var newVal = buf.join('');
                p.mapMaskdigitPositions(newVal, maskDigitPosArr, valLen);
                return newVal;
            },
            mapMaskdigitPositions: function (newVal, maskDigitPosArr, valLen) {
                var maskDiff = options.reverse ? newVal.length - valLen : 0;
                p.maskDigitPosMap = {};
                for (var i = 0; i < maskDigitPosArr.length; i++) {
                    p.maskDigitPosMap[maskDigitPosArr[i] + maskDiff] = 1;
                }
            },
            callbacks: function (e) {
                var val = p.val(),
                    changed = val !== oldValue,
                    defaultArgs = [val, e, el, options],
                    callback = function (name, criteria, args) {
                        if (typeof options[name] === 'function' && criteria) {
                            options[name].apply(this, args);
                        }
                    };

                callback('onChange', changed === true, defaultArgs);
                callback('onKeyPress', changed === true, defaultArgs);
                callback('onComplete', val.length === mask.length, defaultArgs);
                callback('onInvalid', p.invalid.length > 0, [val, e, el, p.invalid, options]);
            }
        };

        el = $(el);
        var jMask = this, oldValue = p.val(), regexMask;

        mask = typeof mask === 'function' ? mask(p.val(), undefined, el, options) : mask;

        // public methods
        jMask.mask = mask;
        jMask.options = options;
        jMask.remove = function () {
            var caret = p.getCaret();
            if (jMask.options.placeholder) {
                el.removeAttr('placeholder');
            }
            if (el.data('mask-maxlength')) {
                el.removeAttr('maxlength');
            }
            p.destroyEvents();
            p.val(jMask.getCleanVal());
            p.setCaret(caret);
            return el;
        };

        // get value without mask
        jMask.getCleanVal = function () {
            return p.getMasked(true);
        };

        // get masked value without the value being in the input or element
        jMask.getMaskedVal = function (val) {
            return p.getMasked(false, val);
        };

        jMask.init = function (onlyMask) {
            onlyMask = onlyMask || false;
            options = options || {};

            jMask.clearIfNotMatch = $.jMaskGlobals.clearIfNotMatch;
            jMask.byPassKeys = $.jMaskGlobals.byPassKeys;
            jMask.translation = $.extend({}, $.jMaskGlobals.translation, options.translation);

            jMask = $.extend(true, {}, jMask, options);

            regexMask = p.getRegexMask();

            if (onlyMask) {
                p.events();
                p.val(p.getMasked());
            } else {
                if (options.placeholder) {
                    el.attr('placeholder', options.placeholder);
                }

                // this is necessary, otherwise if the user submit the form
                // and then press the "back" button, the autocomplete will erase
                // the data. Works fine on IE9+, FF, Opera, Safari.
                if (el.data('mask')) {
                    el.attr('autocomplete', 'off');
                }

                // detect if is necessary let the user type freely.
                // for is a lot faster than forEach.
                for (var i = 0, maxlength = true; i < mask.length; i++) {
                    var translation = jMask.translation[mask.charAt(i)];
                    if (translation && translation.recursive) {
                        maxlength = false;
                        break;
                    }
                }

                if (maxlength) {
                    el.attr('maxlength', mask.length).data('mask-maxlength', true);
                }

                p.destroyEvents();
                p.events();

                var caret = p.getCaret();
                p.val(p.getMasked());
                p.setCaret(caret);
            }
        };

        jMask.init(!el.is('input'));
    };

    $.maskWatchers = {};
    var HTMLAttributes = function () {
        var input = $(this),
            options = {},
            prefix = 'data-mask-',
            mask = input.attr('data-mask');

        if (input.attr(prefix + 'reverse')) {
            options.reverse = true;
        }

        if (input.attr(prefix + 'clearifnotmatch')) {
            options.clearIfNotMatch = true;
        }

        if (input.attr(prefix + 'selectonfocus') === 'true') {
            options.selectOnFocus = true;
        }

        if (notSameMaskObject(input, mask, options)) {
            return input.data('mask', new Mask(this, mask, options));
        }
    },
        notSameMaskObject = function (field, mask, options) {
            options = options || {};
            var maskObject = $(field).data('mask'),
                stringify = JSON.stringify,
                value = $(field).val() || $(field).text();
            try {
                if (typeof mask === 'function') {
                    mask = mask(value);
                }
                return typeof maskObject !== 'object' || stringify(maskObject.options) !== stringify(options) || maskObject.mask !== mask;
            } catch (e) { }
        },
        eventSupported = function (eventName) {
            var el = document.createElement('div'), isSupported;

            eventName = 'on' + eventName;
            isSupported = (eventName in el);

            if (!isSupported) {
                el.setAttribute(eventName, 'return;');
                isSupported = typeof el[eventName] === 'function';
            }
            el = null;

            return isSupported;
        };

    $.fn.mask = function (mask, options) {
        options = options || {};
        var selector = this.selector,
            globals = $.jMaskGlobals,
            interval = globals.watchInterval,
            watchInputs = options.watchInputs || globals.watchInputs,
            maskFunction = function () {
                if (notSameMaskObject(this, mask, options)) {
                    return $(this).data('mask', new Mask(this, mask, options));
                }
            };

        $(this).each(maskFunction);

        if (selector && selector !== '' && watchInputs) {
            clearInterval($.maskWatchers[selector]);
            $.maskWatchers[selector] = setInterval(function () {
                $(document).find(selector).each(maskFunction);
            }, interval);
        }
        return this;
    };

    $.fn.masked = function (val) {
        return this.data('mask').getMaskedVal(val);
    };

    $.fn.unmask = function () {
        clearInterval($.maskWatchers[this.selector]);
        delete $.maskWatchers[this.selector];
        return this.each(function () {
            var dataMask = $(this).data('mask');
            if (dataMask) {
                dataMask.remove().removeData('mask');
            }
        });
    };

    $.fn.cleanVal = function () {
        return this.data('mask').getCleanVal();
    };

    $.applyDataMask = function (selector) {
        selector = selector || $.jMaskGlobals.maskElements;
        var $selector = (selector instanceof $) ? selector : $(selector);
        $selector.filter($.jMaskGlobals.dataMaskAttr).each(HTMLAttributes);
    };

    var globals = {
        maskElements: 'input,td,span,div',
        dataMaskAttr: '*[data-mask]',
        dataMask: true,
        watchInterval: 300,
        watchInputs: true,
        keyStrokeCompensation: 10,
        // old versions of chrome dont work great with input event
        useInput: !/Chrome\/[2-4][0-9]|SamsungBrowser/.test(window.navigator.userAgent) && eventSupported('input'),
        watchDataMask: false,
        byPassKeys: [9, 16, 17, 18, 36, 37, 38, 39, 40, 91],
        translation: {
            '0': { pattern: /\d/ },
            '9': { pattern: /\d/, optional: true },
            '#': { pattern: /\d/, recursive: true },
            'A': { pattern: /[a-zA-Z0-9]/ },
            'S': { pattern: /[a-zA-Z]/ }
        }
    };

    $.jMaskGlobals = $.jMaskGlobals || {};
    globals = $.jMaskGlobals = $.extend(true, {}, globals, $.jMaskGlobals);

    // looking for inputs with data-mask attribute
    if (globals.dataMask) {
        $.applyDataMask();
    }

    setInterval(function () {
        if ($.jMaskGlobals.watchDataMask) {
            $.applyDataMask();
        }
    }, globals.watchInterval);
}, window.jQuery, window.Zepto));;
/*
 * jQuery File Upload Plugin 5.42.2
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2010, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* jshint nomen:false */
/* global define, require, window, document, location, Blob, FormData */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define([
            'jquery',
            'jquery.ui.widget'
        ], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(
            require('jquery'),
            require('./vendor/jquery.ui.widget')
        );
    } else {
        // Browser globals:
        factory(window.jQuery);
    }
}(function ($) {
    'use strict';

    // Detect file input support, based on
    // http://viljamis.com/blog/2012/file-upload-support-on-mobile/
    $.support.fileInput = !(new RegExp(
        // Handle devices which give false positives for the feature detection:
        '(Android (1\\.[0156]|2\\.[01]))' +
            '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
            '|(w(eb)?OSBrowser)|(webOS)' +
            '|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
    ).test(window.navigator.userAgent) ||
        // Feature detection for all other devices:
        $('<input type="file">').prop('disabled'));

    // The FileReader API is not actually used, but works as feature detection,
    // as some Safari versions (5?) support XHR file uploads via the FormData API,
    // but not non-multipart XHR file uploads.
    // window.XMLHttpRequestUpload is not available on IE10, so we check for
    // window.ProgressEvent instead to detect XHR2 file upload capability:
    $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
    $.support.xhrFormDataFileUpload = !!window.FormData;

    // Detect support for Blob slicing (required for chunked uploads):
    $.support.blobSlice = window.Blob && (Blob.prototype.slice ||
        Blob.prototype.webkitSlice || Blob.prototype.mozSlice);

    // Helper function to create drag handlers for dragover/dragenter/dragleave:
    function getDragHandler(type) {
        var isDragOver = type === 'dragover';
        return function (e) {
            e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
            var dataTransfer = e.dataTransfer;
            if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
                    this._trigger(
                        type,
                        $.Event(type, {delegatedEvent: e})
                    ) !== false) {
                e.preventDefault();
                if (isDragOver) {
                    dataTransfer.dropEffect = 'copy';
                }
            }
        };
    }

    // The fileupload widget listens for change events on file input fields defined
    // via fileInput setting and paste or drop events of the given dropZone.
    // In addition to the default jQuery Widget methods, the fileupload widget
    // exposes the "add" and "send" methods, to add or directly send files using
    // the fileupload API.
    // By default, files added via file input selection, paste, drag & drop or
    // "add" method are uploaded immediately, but it is possible to override
    // the "add" callback option to queue file uploads.
    $.widget('blueimp.fileupload', {

        options: {
            // The drop target element(s), by the default the complete document.
            // Set to null to disable drag & drop support:
            dropZone: $(document),
            // The paste target element(s), by the default undefined.
            // Set to a DOM node or jQuery object to enable file pasting:
            pasteZone: undefined,
            // The file input field(s), that are listened to for change events.
            // If undefined, it is set to the file input fields inside
            // of the widget element on plugin initialization.
            // Set to null to disable the change listener.
            fileInput: undefined,
            // By default, the file input field is replaced with a clone after
            // each input field change event. This is required for iframe transport
            // queues and allows change events to be fired for the same file
            // selection, but can be disabled by setting the following option to false:
            replaceFileInput: true,
            // The parameter name for the file form data (the request argument name).
            // If undefined or empty, the name property of the file input field is
            // used, or "files[]" if the file input name property is also empty,
            // can be a string or an array of strings:
            paramName: undefined,
            // By default, each file of a selection is uploaded using an individual
            // request for XHR type uploads. Set to false to upload file
            // selections in one request each:
            singleFileUploads: true,
            // To limit the number of files uploaded with one XHR request,
            // set the following option to an integer greater than 0:
            limitMultiFileUploads: undefined,
            // The following option limits the number of files uploaded with one
            // XHR request to keep the request size under or equal to the defined
            // limit in bytes:
            limitMultiFileUploadSize: undefined,
            // Multipart file uploads add a number of bytes to each uploaded file,
            // therefore the following option adds an overhead for each file used
            // in the limitMultiFileUploadSize configuration:
            limitMultiFileUploadSizeOverhead: 512,
            // Set the following option to true to issue all file upload requests
            // in a sequential order:
            sequentialUploads: false,
            // To limit the number of concurrent uploads,
            // set the following option to an integer greater than 0:
            limitConcurrentUploads: undefined,
            // Set the following option to true to force iframe transport uploads:
            forceIframeTransport: false,
            // Set the following option to the location of a redirect url on the
            // origin server, for cross-domain iframe transport uploads:
            redirect: undefined,
            // The parameter name for the redirect url, sent as part of the form
            // data and set to 'redirect' if this option is empty:
            redirectParamName: undefined,
            // Set the following option to the location of a postMessage window,
            // to enable postMessage transport uploads:
            postMessage: undefined,
            // By default, XHR file uploads are sent as multipart/form-data.
            // The iframe transport is always using multipart/form-data.
            // Set to false to enable non-multipart XHR uploads:
            multipart: true,
            // To upload large files in smaller chunks, set the following option
            // to a preferred maximum chunk size. If set to 0, null or undefined,
            // or the browser does not support the required Blob API, files will
            // be uploaded as a whole.
            maxChunkSize: undefined,
            // When a non-multipart upload or a chunked multipart upload has been
            // aborted, this option can be used to resume the upload by setting
            // it to the size of the already uploaded bytes. This option is most
            // useful when modifying the options object inside of the "add" or
            // "send" callbacks, as the options are cloned for each file upload.
            uploadedBytes: undefined,
            // By default, failed (abort or error) file uploads are removed from the
            // global progress calculation. Set the following option to false to
            // prevent recalculating the global progress data:
            recalculateProgress: true,
            // Interval in milliseconds to calculate and trigger progress events:
            progressInterval: 100,
            // Interval in milliseconds to calculate progress bitrate:
            bitrateInterval: 500,
            // By default, uploads are started automatically when adding files:
            autoUpload: true,

            // Error and info messages:
            messages: {
                uploadedBytes: 'Uploaded bytes exceed file size'
            },

            // Translation function, gets the message key to be translated
            // and an object with context specific data as arguments:
            i18n: function (message, context) {
                message = this.messages[message] || message.toString();
                if (context) {
                    $.each(context, function (key, value) {
                        message = message.replace('{' + key + '}', value);
                    });
                }
                return message;
            },

            // Additional form data to be sent along with the file uploads can be set
            // using this option, which accepts an array of objects with name and
            // value properties, a function returning such an array, a FormData
            // object (for XHR file uploads), or a simple object.
            // The form of the first fileInput is given as parameter to the function:
            formData: function (form) {
                return form.serializeArray();
            },

            // The add callback is invoked as soon as files are added to the fileupload
            // widget (via file input selection, drag & drop, paste or add API call).
            // If the singleFileUploads option is enabled, this callback will be
            // called once for each file in the selection for XHR file uploads, else
            // once for each file selection.
            //
            // The upload starts when the submit method is invoked on the data parameter.
            // The data object contains a files property holding the added files
            // and allows you to override plugin options as well as define ajax settings.
            //
            // Listeners for this callback can also be bound the following way:
            // .bind('fileuploadadd', func);
            //
            // data.submit() returns a Promise object and allows to attach additional
            // handlers using jQuery's Deferred callbacks:
            // data.submit().done(func).fail(func).always(func);
            add: function (e, data) {
                if (e.isDefaultPrevented()) {
                    return false;
                }
                if (data.autoUpload || (data.autoUpload !== false &&
                        $(this).fileupload('option', 'autoUpload'))) {
                    data.process().done(function () {
                        data.submit();
                    });
                }
            },

            // Other callbacks:

            // Callback for the submit event of each file upload:
            // submit: function (e, data) {}, // .bind('fileuploadsubmit', func);

            // Callback for the start of each file upload request:
            // send: function (e, data) {}, // .bind('fileuploadsend', func);

            // Callback for successful uploads:
            // done: function (e, data) {}, // .bind('fileuploaddone', func);

            // Callback for failed (abort or error) uploads:
            // fail: function (e, data) {}, // .bind('fileuploadfail', func);

            // Callback for completed (success, abort or error) requests:
            // always: function (e, data) {}, // .bind('fileuploadalways', func);

            // Callback for upload progress events:
            // progress: function (e, data) {}, // .bind('fileuploadprogress', func);

            // Callback for global upload progress events:
            // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);

            // Callback for uploads start, equivalent to the global ajaxStart event:
            // start: function (e) {}, // .bind('fileuploadstart', func);

            // Callback for uploads stop, equivalent to the global ajaxStop event:
            // stop: function (e) {}, // .bind('fileuploadstop', func);

            // Callback for change events of the fileInput(s):
            // change: function (e, data) {}, // .bind('fileuploadchange', func);

            // Callback for paste events to the pasteZone(s):
            // paste: function (e, data) {}, // .bind('fileuploadpaste', func);

            // Callback for drop events of the dropZone(s):
            // drop: function (e, data) {}, // .bind('fileuploaddrop', func);

            // Callback for dragover events of the dropZone(s):
            // dragover: function (e) {}, // .bind('fileuploaddragover', func);

            // Callback for the start of each chunk upload request:
            // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);

            // Callback for successful chunk uploads:
            // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);

            // Callback for failed (abort or error) chunk uploads:
            // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);

            // Callback for completed (success, abort or error) chunk upload requests:
            // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);

            // The plugin options are used as settings object for the ajax calls.
            // The following are jQuery ajax settings required for the file uploads:
            processData: false,
            contentType: false,
            cache: false
        },

        // A list of options that require reinitializing event listeners and/or
        // special initialization code:
        _specialOptions: [
            'fileInput',
            'dropZone',
            'pasteZone',
            'multipart',
            'forceIframeTransport'
        ],

        _blobSlice: $.support.blobSlice && function () {
            var slice = this.slice || this.webkitSlice || this.mozSlice;
            return slice.apply(this, arguments);
        },

        _BitrateTimer: function () {
            this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
            this.loaded = 0;
            this.bitrate = 0;
            this.getBitrate = function (now, loaded, interval) {
                var timeDiff = now - this.timestamp;
                if (!this.bitrate || !interval || timeDiff > interval) {
                    this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
                    this.loaded = loaded;
                    this.timestamp = now;
                }
                return this.bitrate;
            };
        },

        _isXHRUpload: function (options) {
            return !options.forceIframeTransport &&
                ((!options.multipart && $.support.xhrFileUpload) ||
                $.support.xhrFormDataFileUpload);
        },

        _getFormData: function (options) {
            var formData;
            if ($.type(options.formData) === 'function') {
                return options.formData(options.form);
            }
            if ($.isArray(options.formData)) {
                return options.formData;
            }
            if ($.type(options.formData) === 'object') {
                formData = [];
                $.each(options.formData, function (name, value) {
                    formData.push({name: name, value: value});
                });
                return formData;
            }
            return [];
        },

        _getTotal: function (files) {
            var total = 0;
            $.each(files, function (index, file) {
                total += file.size || 1;
            });
            return total;
        },

        _initProgressObject: function (obj) {
            var progress = {
                loaded: 0,
                total: 0,
                bitrate: 0
            };
            if (obj._progress) {
                $.extend(obj._progress, progress);
            } else {
                obj._progress = progress;
            }
        },

        _initResponseObject: function (obj) {
            var prop;
            if (obj._response) {
                for (prop in obj._response) {
                    if (obj._response.hasOwnProperty(prop)) {
                        delete obj._response[prop];
                    }
                }
            } else {
                obj._response = {};
            }
        },

        _onProgress: function (e, data) {
            if (e.lengthComputable) {
                var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
                    loaded;
                if (data._time && data.progressInterval &&
                        (now - data._time < data.progressInterval) &&
                        e.loaded !== e.total) {
                    return;
                }
                data._time = now;
                loaded = Math.floor(
                    e.loaded / e.total * (data.chunkSize || data._progress.total)
                ) + (data.uploadedBytes || 0);
                // Add the difference from the previously loaded state
                // to the global loaded counter:
                this._progress.loaded += (loaded - data._progress.loaded);
                this._progress.bitrate = this._bitrateTimer.getBitrate(
                    now,
                    this._progress.loaded,
                    data.bitrateInterval
                );
                data._progress.loaded = data.loaded = loaded;
                data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
                    now,
                    loaded,
                    data.bitrateInterval
                );
                // Trigger a custom progress event with a total data property set
                // to the file size(s) of the current upload and a loaded data
                // property calculated accordingly:
                this._trigger(
                    'progress',
                    $.Event('progress', {delegatedEvent: e}),
                    data
                );
                // Trigger a global progress event for all current file uploads,
                // including ajax calls queued for sequential file uploads:
                this._trigger(
                    'progressall',
                    $.Event('progressall', {delegatedEvent: e}),
                    this._progress
                );
            }
        },

        _initProgressListener: function (options) {
            var that = this,
                xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
            // Accesss to the native XHR object is required to add event listeners
            // for the upload progress event:
            if (xhr.upload) {
                $(xhr.upload).bind('progress', function (e) {
                    var oe = e.originalEvent;
                    // Make sure the progress event properties get copied over:
                    e.lengthComputable = oe.lengthComputable;
                    e.loaded = oe.loaded;
                    e.total = oe.total;
                    that._onProgress(e, options);
                });
                options.xhr = function () {
                    return xhr;
                };
            }
        },

        _isInstanceOf: function (type, obj) {
            // Cross-frame instanceof check
            return Object.prototype.toString.call(obj) === '[object ' + type + ']';
        },

        _initXHRData: function (options) {
            var that = this,
                formData,
                file = options.files[0],
                // Ignore non-multipart setting if not supported:
                multipart = options.multipart || !$.support.xhrFileUpload,
                paramName = $.type(options.paramName) === 'array' ?
                    options.paramName[0] : options.paramName;
            options.headers = $.extend({}, options.headers);
            if (options.contentRange) {
                options.headers['Content-Range'] = options.contentRange;
            }
            if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
                options.headers['Content-Disposition'] = 'attachment; filename="' +
                    encodeURI(file.name) + '"';
            }
            if (!multipart) {
                options.contentType = file.type || 'application/octet-stream';
                options.data = options.blob || file;
            } else if ($.support.xhrFormDataFileUpload) {
                if (options.postMessage) {
                    // window.postMessage does not allow sending FormData
                    // objects, so we just add the File/Blob objects to
                    // the formData array and let the postMessage window
                    // create the FormData object out of this array:
                    formData = this._getFormData(options);
                    if (options.blob) {
                        formData.push({
                            name: paramName,
                            value: options.blob
                        });
                    } else {
                        $.each(options.files, function (index, file) {
                            formData.push({
                                name: ($.type(options.paramName) === 'array' &&
                                    options.paramName[index]) || paramName,
                                value: file
                            });
                        });
                    }
                } else {
                    if (that._isInstanceOf('FormData', options.formData)) {
                        formData = options.formData;
                    } else {
                        formData = new FormData();
                        $.each(this._getFormData(options), function (index, field) {
                            formData.append(field.name, field.value);
                        });
                    }
                    if (options.blob) {
                        formData.append(paramName, options.blob, file.name);
                    } else {
                        $.each(options.files, function (index, file) {
                            // This check allows the tests to run with
                            // dummy objects:
                            if (that._isInstanceOf('File', file) ||
                                    that._isInstanceOf('Blob', file)) {
                                formData.append(
                                    ($.type(options.paramName) === 'array' &&
                                        options.paramName[index]) || paramName,
                                    file,
                                    file.uploadName || file.name
                                );
                            }
                        });
                    }
                }
                options.data = formData;
            }
            // Blob reference is not needed anymore, free memory:
            options.blob = null;
        },

        _initIframeSettings: function (options) {
            var targetHost = $('<a></a>').prop('href', options.url).prop('host');
            // Setting the dataType to iframe enables the iframe transport:
            options.dataType = 'iframe ' + (options.dataType || '');
            // The iframe transport accepts a serialized array as form data:
            options.formData = this._getFormData(options);
            // Add redirect url to form data on cross-domain uploads:
            if (options.redirect && targetHost && targetHost !== location.host) {
                options.formData.push({
                    name: options.redirectParamName || 'redirect',
                    value: options.redirect
                });
            }
        },

        _initDataSettings: function (options) {
            if (this._isXHRUpload(options)) {
                if (!this._chunkedUpload(options, true)) {
                    if (!options.data) {
                        this._initXHRData(options);
                    }
                    this._initProgressListener(options);
                }
                if (options.postMessage) {
                    // Setting the dataType to postmessage enables the
                    // postMessage transport:
                    options.dataType = 'postmessage ' + (options.dataType || '');
                }
            } else {
                this._initIframeSettings(options);
            }
        },

        _getParamName: function (options) {
            var fileInput = $(options.fileInput),
                paramName = options.paramName;
            if (!paramName) {
                paramName = [];
                fileInput.each(function () {
                    var input = $(this),
                        name = input.prop('name') || 'files[]',
                        i = (input.prop('files') || [1]).length;
                    while (i) {
                        paramName.push(name);
                        i -= 1;
                    }
                });
                if (!paramName.length) {
                    paramName = [fileInput.prop('name') || 'files[]'];
                }
            } else if (!$.isArray(paramName)) {
                paramName = [paramName];
            }
            return paramName;
        },

        _initFormSettings: function (options) {
            // Retrieve missing options from the input field and the
            // associated form, if available:
            if (!options.form || !options.form.length) {
                options.form = $(options.fileInput.prop('form'));
                // If the given file input doesn't have an associated form,
                // use the default widget file input's form:
                if (!options.form.length) {
                    options.form = $(this.options.fileInput.prop('form'));
                }
            }
            options.paramName = this._getParamName(options);
            if (!options.url) {
                options.url = options.form.prop('action') || location.href;
            }
            // The HTTP request method must be "POST" or "PUT":
            options.type = (options.type ||
                ($.type(options.form.prop('method')) === 'string' &&
                    options.form.prop('method')) || ''
                ).toUpperCase();
            if (options.type !== 'POST' && options.type !== 'PUT' &&
                    options.type !== 'PATCH') {
                options.type = 'POST';
            }
            if (!options.formAcceptCharset) {
                options.formAcceptCharset = options.form.attr('accept-charset');
            }
        },

        _getAJAXSettings: function (data) {
            var options = $.extend({}, this.options, data);
            this._initFormSettings(options);
            this._initDataSettings(options);
            return options;
        },

        // jQuery 1.6 doesn't provide .state(),
        // while jQuery 1.8+ removed .isRejected() and .isResolved():
        _getDeferredState: function (deferred) {
            if (deferred.state) {
                return deferred.state();
            }
            if (deferred.isResolved()) {
                return 'resolved';
            }
            if (deferred.isRejected()) {
                return 'rejected';
            }
            return 'pending';
        },

        // Maps jqXHR callbacks to the equivalent
        // methods of the given Promise object:
        _enhancePromise: function (promise) {
            promise.success = promise.done;
            promise.error = promise.fail;
            promise.complete = promise.always;
            return promise;
        },

        // Creates and returns a Promise object enhanced with
        // the jqXHR methods abort, success, error and complete:
        _getXHRPromise: function (resolveOrReject, context, args) {
            var dfd = $.Deferred(),
                promise = dfd.promise();
            context = context || this.options.context || promise;
            if (resolveOrReject === true) {
                dfd.resolveWith(context, args);
            } else if (resolveOrReject === false) {
                dfd.rejectWith(context, args);
            }
            promise.abort = dfd.promise;
            return this._enhancePromise(promise);
        },

        // Adds convenience methods to the data callback argument:
        _addConvenienceMethods: function (e, data) {
            var that = this,
                getPromise = function (args) {
                    return $.Deferred().resolveWith(that, args).promise();
                };
            data.process = function (resolveFunc, rejectFunc) {
                if (resolveFunc || rejectFunc) {
                    data._processQueue = this._processQueue =
                        (this._processQueue || getPromise([this])).pipe(
                            function () {
                                if (data.errorThrown) {
                                    return $.Deferred()
                                        .rejectWith(that, [data]).promise();
                                }
                                return getPromise(arguments);
                            }
                        ).pipe(resolveFunc, rejectFunc);
                }
                return this._processQueue || getPromise([this]);
            };
            data.submit = function () {
                if (this.state() !== 'pending') {
                    data.jqXHR = this.jqXHR =
                        (that._trigger(
                            'submit',
                            $.Event('submit', {delegatedEvent: e}),
                            this
                        ) !== false) && that._onSend(e, this);
                }
                return this.jqXHR || that._getXHRPromise();
            };
            data.abort = function () {
                if (this.jqXHR) {
                    return this.jqXHR.abort();
                }
                this.errorThrown = 'abort';
                that._trigger('fail', null, this);
                return that._getXHRPromise(false);
            };
            data.state = function () {
                if (this.jqXHR) {
                    return that._getDeferredState(this.jqXHR);
                }
                if (this._processQueue) {
                    return that._getDeferredState(this._processQueue);
                }
            };
            data.processing = function () {
                return !this.jqXHR && this._processQueue && that
                    ._getDeferredState(this._processQueue) === 'pending';
            };
            data.progress = function () {
                return this._progress;
            };
            data.response = function () {
                return this._response;
            };
        },

        // Parses the Range header from the server response
        // and returns the uploaded bytes:
        _getUploadedBytes: function (jqXHR) {
            var range = jqXHR.getResponseHeader('Range'),
                parts = range && range.split('-'),
                upperBytesPos = parts && parts.length > 1 &&
                    parseInt(parts[1], 10);
            return upperBytesPos && upperBytesPos + 1;
        },

        // Uploads a file in multiple, sequential requests
        // by splitting the file up in multiple blob chunks.
        // If the second parameter is true, only tests if the file
        // should be uploaded in chunks, but does not invoke any
        // upload requests:
        _chunkedUpload: function (options, testOnly) {
            options.uploadedBytes = options.uploadedBytes || 0;
            var that = this,
                file = options.files[0],
                fs = file.size,
                ub = options.uploadedBytes,
                mcs = options.maxChunkSize || fs,
                slice = this._blobSlice,
                dfd = $.Deferred(),
                promise = dfd.promise(),
                jqXHR,
                upload;
            if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) ||
                    options.data) {
                return false;
            }
            if (testOnly) {
                return true;
            }
            if (ub >= fs) {
                file.error = options.i18n('uploadedBytes');
                return this._getXHRPromise(
                    false,
                    options.context,
                    [null, 'error', file.error]
                );
            }
            // The chunk upload method:
            upload = function () {
                // Clone the options object for each chunk upload:
                var o = $.extend({}, options),
                    currentLoaded = o._progress.loaded;
                o.blob = slice.call(
                    file,
                    ub,
                    ub + mcs,
                    file.type
                );
                // Store the current chunk size, as the blob itself
                // will be dereferenced after data processing:
                o.chunkSize = o.blob.size;
                // Expose the chunk bytes position range:
                o.contentRange = 'bytes ' + ub + '-' +
                    (ub + o.chunkSize - 1) + '/' + fs;
                // Process the upload data (the blob and potential form data):
                that._initXHRData(o);
                // Add progress listeners for this chunk upload:
                that._initProgressListener(o);
                jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
                        that._getXHRPromise(false, o.context))
                    .done(function (result, textStatus, jqXHR) {
                        ub = that._getUploadedBytes(jqXHR) ||
                            (ub + o.chunkSize);
                        // Create a progress event if no final progress event
                        // with loaded equaling total has been triggered
                        // for this chunk:
                        if (currentLoaded + o.chunkSize - o._progress.loaded) {
                            that._onProgress($.Event('progress', {
                                lengthComputable: true,
                                loaded: ub - o.uploadedBytes,
                                total: ub - o.uploadedBytes
                            }), o);
                        }
                        options.uploadedBytes = o.uploadedBytes = ub;
                        o.result = result;
                        o.textStatus = textStatus;
                        o.jqXHR = jqXHR;
                        that._trigger('chunkdone', null, o);
                        that._trigger('chunkalways', null, o);
                        if (ub < fs) {
                            // File upload not yet complete,
                            // continue with the next chunk:
                            upload();
                        } else {
                            dfd.resolveWith(
                                o.context,
                                [result, textStatus, jqXHR]
                            );
                        }
                    })
                    .fail(function (jqXHR, textStatus, errorThrown) {
                        o.jqXHR = jqXHR;
                        o.textStatus = textStatus;
                        o.errorThrown = errorThrown;
                        that._trigger('chunkfail', null, o);
                        that._trigger('chunkalways', null, o);
                        dfd.rejectWith(
                            o.context,
                            [jqXHR, textStatus, errorThrown]
                        );
                    });
            };
            this._enhancePromise(promise);
            promise.abort = function () {
                return jqXHR.abort();
            };
            upload();
            return promise;
        },

        _beforeSend: function (e, data) {
            if (this._active === 0) {
                // the start callback is triggered when an upload starts
                // and no other uploads are currently running,
                // equivalent to the global ajaxStart event:
                this._trigger('start');
                // Set timer for global bitrate progress calculation:
                this._bitrateTimer = new this._BitrateTimer();
                // Reset the global progress values:
                this._progress.loaded = this._progress.total = 0;
                this._progress.bitrate = 0;
            }
            // Make sure the container objects for the .response() and
            // .progress() methods on the data object are available
            // and reset to their initial state:
            this._initResponseObject(data);
            this._initProgressObject(data);
            data._progress.loaded = data.loaded = data.uploadedBytes || 0;
            data._progress.total = data.total = this._getTotal(data.files) || 1;
            data._progress.bitrate = data.bitrate = 0;
            this._active += 1;
            // Initialize the global progress values:
            this._progress.loaded += data.loaded;
            this._progress.total += data.total;
        },

        _onDone: function (result, textStatus, jqXHR, options) {
            var total = options._progress.total,
                response = options._response;
            if (options._progress.loaded < total) {
                // Create a progress event if no final progress event
                // with loaded equaling total has been triggered:
                this._onProgress($.Event('progress', {
                    lengthComputable: true,
                    loaded: total,
                    total: total
                }), options);
            }
            response.result = options.result = result;
            response.textStatus = options.textStatus = textStatus;
            response.jqXHR = options.jqXHR = jqXHR;
            this._trigger('done', null, options);
        },

        _onFail: function (jqXHR, textStatus, errorThrown, options) {
            var response = options._response;
            if (options.recalculateProgress) {
                // Remove the failed (error or abort) file upload from
                // the global progress calculation:
                this._progress.loaded -= options._progress.loaded;
                this._progress.total -= options._progress.total;
            }
            response.jqXHR = options.jqXHR = jqXHR;
            response.textStatus = options.textStatus = textStatus;
            response.errorThrown = options.errorThrown = errorThrown;
            this._trigger('fail', null, options);
        },

        _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
            // jqXHRorResult, textStatus and jqXHRorError are added to the
            // options object via done and fail callbacks
            this._trigger('always', null, options);
        },

        _onSend: function (e, data) {
            if (!data.submit) {
                this._addConvenienceMethods(e, data);
            }
            var that = this,
                jqXHR,
                aborted,
                slot,
                pipe,
                options = that._getAJAXSettings(data),
                send = function () {
                    that._sending += 1;
                    // Set timer for bitrate progress calculation:
                    options._bitrateTimer = new that._BitrateTimer();
                    jqXHR = jqXHR || (
                        ((aborted || that._trigger(
                            'send',
                            $.Event('send', {delegatedEvent: e}),
                            options
                        ) === false) &&
                        that._getXHRPromise(false, options.context, aborted)) ||
                        that._chunkedUpload(options) || $.ajax(options)
                    ).done(function (result, textStatus, jqXHR) {
                        that._onDone(result, textStatus, jqXHR, options);
                    }).fail(function (jqXHR, textStatus, errorThrown) {
                        that._onFail(jqXHR, textStatus, errorThrown, options);
                    }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
                        that._onAlways(
                            jqXHRorResult,
                            textStatus,
                            jqXHRorError,
                            options
                        );
                        that._sending -= 1;
                        that._active -= 1;
                        if (options.limitConcurrentUploads &&
                                options.limitConcurrentUploads > that._sending) {
                            // Start the next queued upload,
                            // that has not been aborted:
                            var nextSlot = that._slots.shift();
                            while (nextSlot) {
                                if (that._getDeferredState(nextSlot) === 'pending') {
                                    nextSlot.resolve();
                                    break;
                                }
                                nextSlot = that._slots.shift();
                            }
                        }
                        if (that._active === 0) {
                            // The stop callback is triggered when all uploads have
                            // been completed, equivalent to the global ajaxStop event:
                            that._trigger('stop');
                        }
                    });
                    return jqXHR;
                };
            this._beforeSend(e, options);
            if (this.options.sequentialUploads ||
                    (this.options.limitConcurrentUploads &&
                    this.options.limitConcurrentUploads <= this._sending)) {
                if (this.options.limitConcurrentUploads > 1) {
                    slot = $.Deferred();
                    this._slots.push(slot);
                    pipe = slot.pipe(send);
                } else {
                    this._sequence = this._sequence.pipe(send, send);
                    pipe = this._sequence;
                }
                // Return the piped Promise object, enhanced with an abort method,
                // which is delegated to the jqXHR object of the current upload,
                // and jqXHR callbacks mapped to the equivalent Promise methods:
                pipe.abort = function () {
                    aborted = [undefined, 'abort', 'abort'];
                    if (!jqXHR) {
                        if (slot) {
                            slot.rejectWith(options.context, aborted);
                        }
                        return send();
                    }
                    return jqXHR.abort();
                };
                return this._enhancePromise(pipe);
            }
            return send();
        },

        _onAdd: function (e, data) {
            var that = this,
                result = true,
                options = $.extend({}, this.options, data),
                files = data.files,
                filesLength = files.length,
                limit = options.limitMultiFileUploads,
                limitSize = options.limitMultiFileUploadSize,
                overhead = options.limitMultiFileUploadSizeOverhead,
                batchSize = 0,
                paramName = this._getParamName(options),
                paramNameSet,
                paramNameSlice,
                fileSet,
                i,
                j = 0;
            if (limitSize && (!filesLength || files[0].size === undefined)) {
                limitSize = undefined;
            }
            if (!(options.singleFileUploads || limit || limitSize) ||
                    !this._isXHRUpload(options)) {
                fileSet = [files];
                paramNameSet = [paramName];
            } else if (!(options.singleFileUploads || limitSize) && limit) {
                fileSet = [];
                paramNameSet = [];
                for (i = 0; i < filesLength; i += limit) {
                    fileSet.push(files.slice(i, i + limit));
                    paramNameSlice = paramName.slice(i, i + limit);
                    if (!paramNameSlice.length) {
                        paramNameSlice = paramName;
                    }
                    paramNameSet.push(paramNameSlice);
                }
            } else if (!options.singleFileUploads && limitSize) {
                fileSet = [];
                paramNameSet = [];
                for (i = 0; i < filesLength; i = i + 1) {
                    batchSize += files[i].size + overhead;
                    if (i + 1 === filesLength ||
                            ((batchSize + files[i + 1].size + overhead) > limitSize) ||
                            (limit && i + 1 - j >= limit)) {
                        fileSet.push(files.slice(j, i + 1));
                        paramNameSlice = paramName.slice(j, i + 1);
                        if (!paramNameSlice.length) {
                            paramNameSlice = paramName;
                        }
                        paramNameSet.push(paramNameSlice);
                        j = i + 1;
                        batchSize = 0;
                    }
                }
            } else {
                paramNameSet = paramName;
            }
            data.originalFiles = files;
            $.each(fileSet || files, function (index, element) {
                var newData = $.extend({}, data);
                newData.files = fileSet ? element : [element];
                newData.paramName = paramNameSet[index];
                that._initResponseObject(newData);
                that._initProgressObject(newData);
                that._addConvenienceMethods(e, newData);
                result = that._trigger(
                    'add',
                    $.Event('add', {delegatedEvent: e}),
                    newData
                );
                return result;
            });
            return result;
        },

        _replaceFileInput: function (data) {
            var input = data.fileInput,
                inputClone = input.clone(true);
            // Add a reference for the new cloned file input to the data argument:
            data.fileInputClone = inputClone;
            $('<form></form>').append(inputClone)[0].reset();
            // Detaching allows to insert the fileInput on another form
            // without loosing the file input value:
            input.after(inputClone).detach();
            // Avoid memory leaks with the detached file input:
            $.cleanData(input.unbind('remove'));
            // Replace the original file input element in the fileInput
            // elements set with the clone, which has been copied including
            // event handlers:
            this.options.fileInput = this.options.fileInput.map(function (i, el) {
                if (el === input[0]) {
                    return inputClone[0];
                }
                return el;
            });
            // If the widget has been initialized on the file input itself,
            // override this.element with the file input clone:
            if (input[0] === this.element[0]) {
                this.element = inputClone;
            }
        },

        _handleFileTreeEntry: function (entry, path) {
            var that = this,
                dfd = $.Deferred(),
                errorHandler = function (e) {
                    if (e && !e.entry) {
                        e.entry = entry;
                    }
                    // Since $.when returns immediately if one
                    // Deferred is rejected, we use resolve instead.
                    // This allows valid files and invalid items
                    // to be returned together in one set:
                    dfd.resolve([e]);
                },
                successHandler = function (entries) {
                    that._handleFileTreeEntries(
                        entries,
                        path + entry.name + '/'
                    ).done(function (files) {
                        dfd.resolve(files);
                    }).fail(errorHandler);
                },
                readEntries = function () {
                    dirReader.readEntries(function (results) {
                        if (!results.length) {
                            successHandler(entries);
                        } else {
                            entries = entries.concat(results);
                            readEntries();
                        }
                    }, errorHandler);
                },
                dirReader, entries = [];
            path = path || '';
            if (entry.isFile) {
                if (entry._file) {
                    // Workaround for Chrome bug #149735
                    entry._file.relativePath = path;
                    dfd.resolve(entry._file);
                } else {
                    entry.file(function (file) {
                        file.relativePath = path;
                        dfd.resolve(file);
                    }, errorHandler);
                }
            } else if (entry.isDirectory) {
                dirReader = entry.createReader();
                readEntries();
            } else {
                // Return an empy list for file system items
                // other than files or directories:
                dfd.resolve([]);
            }
            return dfd.promise();
        },

        _handleFileTreeEntries: function (entries, path) {
            var that = this;
            return $.when.apply(
                $,
                $.map(entries, function (entry) {
                    return that._handleFileTreeEntry(entry, path);
                })
            ).pipe(function () {
                return Array.prototype.concat.apply(
                    [],
                    arguments
                );
            });
        },

        _getDroppedFiles: function (dataTransfer) {
            dataTransfer = dataTransfer || {};
            var items = dataTransfer.items;
            if (items && items.length && (items[0].webkitGetAsEntry ||
                    items[0].getAsEntry)) {
                return this._handleFileTreeEntries(
                    $.map(items, function (item) {
                        var entry;
                        if (item.webkitGetAsEntry) {
                            entry = item.webkitGetAsEntry();
                            if (entry) {
                                // Workaround for Chrome bug #149735:
                                entry._file = item.getAsFile();
                            }
                            return entry;
                        }
                        return item.getAsEntry();
                    })
                );
            }
            return $.Deferred().resolve(
                $.makeArray(dataTransfer.files)
            ).promise();
        },

        _getSingleFileInputFiles: function (fileInput) {
            fileInput = $(fileInput);
            var entries = fileInput.prop('webkitEntries') ||
                    fileInput.prop('entries'),
                files,
                value;
            if (entries && entries.length) {
                return this._handleFileTreeEntries(entries);
            }
            files = $.makeArray(fileInput.prop('files'));
            if (!files.length) {
                value = fileInput.prop('value');
                if (!value) {
                    return $.Deferred().resolve([]).promise();
                }
                // If the files property is not available, the browser does not
                // support the File API and we add a pseudo File object with
                // the input value as name with path information removed:
                files = [{name: value.replace(/^.*\\/, '')}];
            } else if (files[0].name === undefined && files[0].fileName) {
                // File normalization for Safari 4 and Firefox 3:
                $.each(files, function (index, file) {
                    file.name = file.fileName;
                    file.size = file.fileSize;
                });
            }
            return $.Deferred().resolve(files).promise();
        },

        _getFileInputFiles: function (fileInput) {
            if (!(fileInput instanceof $) || fileInput.length === 1) {
                return this._getSingleFileInputFiles(fileInput);
            }
            return $.when.apply(
                $,
                $.map(fileInput, this._getSingleFileInputFiles)
            ).pipe(function () {
                return Array.prototype.concat.apply(
                    [],
                    arguments
                );
            });
        },

        _onChange: function (e) {
            var that = this,
                data = {
                    fileInput: $(e.target),
                    form: $(e.target.form)
                };
            this._getFileInputFiles(data.fileInput).always(function (files) {
                data.files = files;
                if (that.options.replaceFileInput) {
                    that._replaceFileInput(data);
                }
                if (that._trigger(
                        'change',
                        $.Event('change', {delegatedEvent: e}),
                        data
                    ) !== false) {
                    that._onAdd(e, data);
                }
            });
        },

        _onPaste: function (e) {
            var items = e.originalEvent && e.originalEvent.clipboardData &&
                    e.originalEvent.clipboardData.items,
                data = {files: []};
            if (items && items.length) {
                $.each(items, function (index, item) {
                    var file = item.getAsFile && item.getAsFile();
                    if (file) {
                        data.files.push(file);
                    }
                });
                if (this._trigger(
                        'paste',
                        $.Event('paste', {delegatedEvent: e}),
                        data
                    ) !== false) {
                    this._onAdd(e, data);
                }
            }
        },

        _onDrop: function (e) {
            e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
            var that = this,
                dataTransfer = e.dataTransfer,
                data = {};
            if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
                e.preventDefault();
                this._getDroppedFiles(dataTransfer).always(function (files) {
                    data.files = files;
                    if (that._trigger(
                            'drop',
                            $.Event('drop', {delegatedEvent: e}),
                            data
                        ) !== false) {
                        that._onAdd(e, data);
                    }
                });
            }
        },

        _onDragOver: getDragHandler('dragover'),

        _onDragEnter: getDragHandler('dragenter'),

        _onDragLeave: getDragHandler('dragleave'),

        _initEventHandlers: function () {
            if (this._isXHRUpload(this.options)) {
                this._on(this.options.dropZone, {
                    dragover: this._onDragOver,
                    drop: this._onDrop,
                    // event.preventDefault() on dragenter is required for IE10+:
                    dragenter: this._onDragEnter,
                    // dragleave is not required, but added for completeness:
                    dragleave: this._onDragLeave
                });
                this._on(this.options.pasteZone, {
                    paste: this._onPaste
                });
            }
            if ($.support.fileInput) {
                this._on(this.options.fileInput, {
                    change: this._onChange
                });
            }
        },

        _destroyEventHandlers: function () {
            this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
            this._off(this.options.pasteZone, 'paste');
            this._off(this.options.fileInput, 'change');
        },

        _setOption: function (key, value) {
            var reinit = $.inArray(key, this._specialOptions) !== -1;
            if (reinit) {
                this._destroyEventHandlers();
            }
            this._super(key, value);
            if (reinit) {
                this._initSpecialOptions();
                this._initEventHandlers();
            }
        },

        _initSpecialOptions: function () {
            var options = this.options;
            if (options.fileInput === undefined) {
                options.fileInput = this.element.is('input[type="file"]') ?
                        this.element : this.element.find('input[type="file"]');
            } else if (!(options.fileInput instanceof $)) {
                options.fileInput = $(options.fileInput);
            }
            if (!(options.dropZone instanceof $)) {
                options.dropZone = $(options.dropZone);
            }
            if (!(options.pasteZone instanceof $)) {
                options.pasteZone = $(options.pasteZone);
            }
        },

        _getRegExp: function (str) {
            var parts = str.split('/'),
                modifiers = parts.pop();
            parts.shift();
            return new RegExp(parts.join('/'), modifiers);
        },

        _isRegExpOption: function (key, value) {
            return key !== 'url' && $.type(value) === 'string' &&
                /^\/.*\/[igm]{0,3}$/.test(value);
        },

        _initDataAttributes: function () {
            var that = this,
                options = this.options,
                clone = $(this.element[0].cloneNode(false)),
                data = clone.data();
            // Avoid memory leaks:
            clone.remove();
            // Initialize options set via HTML5 data-attributes:
            $.each(
                data,
                function (key, value) {
                    var dataAttributeName = 'data-' +
                        // Convert camelCase to hyphen-ated key:
                        key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
                    if (clone.attr(dataAttributeName)) {
                        if (that._isRegExpOption(key, value)) {
                            value = that._getRegExp(value);
                        }
                        options[key] = value;
                    }
                }
            );
        },

        _create: function () {
            this._initDataAttributes();
            this._initSpecialOptions();
            this._slots = [];
            this._sequence = this._getXHRPromise(true);
            this._sending = this._active = 0;
            this._initProgressObject(this);
            this._initEventHandlers();
        },

        // This method is exposed to the widget API and allows to query
        // the number of active uploads:
        active: function () {
            return this._active;
        },

        // This method is exposed to the widget API and allows to query
        // the widget upload progress.
        // It returns an object with loaded, total and bitrate properties
        // for the running uploads:
        progress: function () {
            return this._progress;
        },

        // This method is exposed to the widget API and allows adding files
        // using the fileupload API. The data parameter accepts an object which
        // must have a files property and can contain additional options:
        // .fileupload('add', {files: filesList});
        add: function (data) {
            var that = this;
            if (!data || this.options.disabled) {
                return;
            }
            if (data.fileInput && !data.files) {
                this._getFileInputFiles(data.fileInput).always(function (files) {
                    data.files = files;
                    that._onAdd(null, data);
                });
            } else {
                data.files = $.makeArray(data.files);
                this._onAdd(null, data);
            }
        },

        // This method is exposed to the widget API and allows sending files
        // using the fileupload API. The data parameter accepts an object which
        // must have a files or fileInput property and can contain additional options:
        // .fileupload('send', {files: filesList});
        // The method returns a Promise object for the file upload call.
        send: function (data) {
            if (data && !this.options.disabled) {
                if (data.fileInput && !data.files) {
                    var that = this,
                        dfd = $.Deferred(),
                        promise = dfd.promise(),
                        jqXHR,
                        aborted;
                    promise.abort = function () {
                        aborted = true;
                        if (jqXHR) {
                            return jqXHR.abort();
                        }
                        dfd.reject(null, 'abort', 'abort');
                        return promise;
                    };
                    this._getFileInputFiles(data.fileInput).always(
                        function (files) {
                            if (aborted) {
                                return;
                            }
                            if (!files.length) {
                                dfd.reject();
                                return;
                            }
                            data.files = files;
                            jqXHR = that._onSend(null, data);
                            jqXHR.then(
                                function (result, textStatus, jqXHR) {
                                    dfd.resolve(result, textStatus, jqXHR);
                                },
                                function (jqXHR, textStatus, errorThrown) {
                                    dfd.reject(jqXHR, textStatus, errorThrown);
                                }
                            );
                        }
                    );
                    return this._enhancePromise(promise);
                }
                data.files = $.makeArray(data.files);
                if (data.files.length) {
                    return this._onSend(null, data);
                }
            }
            return this._getXHRPromise(false, data && data.context);
        }

    });

}));
;
/*
 * jQuery Iframe Transport Plugin 1.8.3
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

/* global define, require, window, document */

(function (factory) {
    'use strict';
    if (typeof define === 'function' && define.amd) {
        // Register as an anonymous AMD module:
        define(['jquery'], factory);
    } else if (typeof exports === 'object') {
        // Node/CommonJS:
        factory(require('jquery'));
    } else {
        // Browser globals:
        factory(window.jQuery);
    }
}(function ($) {
    'use strict';

    // Helper variable to create unique names for the transport iframes:
    var counter = 0;

    // The iframe transport accepts four additional options:
    // options.fileInput: a jQuery collection of file input fields
    // options.paramName: the parameter name for the file form data,
    //  overrides the name property of the file input field(s),
    //  can be a string or an array of strings.
    // options.formData: an array of objects with name and value properties,
    //  equivalent to the return data of .serializeArray(), e.g.:
    //  [{name: 'a', value: 1}, {name: 'b', value: 2}]
    // options.initialIframeSrc: the URL of the initial iframe src,
    //  by default set to "javascript:false;"
    $.ajaxTransport('iframe', function (options) {
        if (options.async) {
            // javascript:false as initial iframe src
            // prevents warning popups on HTTPS in IE6:
            /*jshint scripturl: true */
            var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
            /*jshint scripturl: false */
                form,
                iframe,
                addParamChar;
            return {
                send: function (_, completeCallback) {
                    form = $('<form style="display:none;"></form>');
                    form.attr('accept-charset', options.formAcceptCharset);
                    addParamChar = /\?/.test(options.url) ? '&' : '?';
                    // XDomainRequest only supports GET and POST:
                    if (options.type === 'DELETE') {
                        options.url = options.url + addParamChar + '_method=DELETE';
                        options.type = 'POST';
                    } else if (options.type === 'PUT') {
                        options.url = options.url + addParamChar + '_method=PUT';
                        options.type = 'POST';
                    } else if (options.type === 'PATCH') {
                        options.url = options.url + addParamChar + '_method=PATCH';
                        options.type = 'POST';
                    }
                    // IE versions below IE8 cannot set the name property of
                    // elements that have already been added to the DOM,
                    // so we set the name along with the iframe HTML markup:
                    counter += 1;
                    iframe = $(
                        '<iframe src="' + initialIframeSrc +
                            '" name="iframe-transport-' + counter + '"></iframe>'
                    ).bind('load', function () {
                        var fileInputClones,
                            paramNames = $.isArray(options.paramName) ?
                                    options.paramName : [options.paramName];
                        iframe
                            .unbind('load')
                            .bind('load', function () {
                                var response;
                                // Wrap in a try/catch block to catch exceptions thrown
                                // when trying to access cross-domain iframe contents:
                                try {
                                    response = iframe.contents();
                                    // Google Chrome and Firefox do not throw an
                                    // exception when calling iframe.contents() on
                                    // cross-domain requests, so we unify the response:
                                    if (!response.length || !response[0].firstChild) {
                                        throw new Error();
                                    }
                                } catch (e) {
                                    response = undefined;
                                }
                                // The complete callback returns the
                                // iframe content document as response object:
                                completeCallback(
                                    200,
                                    'success',
                                    {'iframe': response}
                                );
                                // Fix for IE endless progress bar activity bug
                                // (happens on form submits to iframe targets):
                                $('<iframe src="' + initialIframeSrc + '"></iframe>')
                                    .appendTo(form);
                                window.setTimeout(function () {
                                    // Removing the form in a setTimeout call
                                    // allows Chrome's developer tools to display
                                    // the response result
                                    form.remove();
                                }, 0);
                            });
                        form
                            .prop('target', iframe.prop('name'))
                            .prop('action', options.url)
                            .prop('method', options.type);
                        if (options.formData) {
                            $.each(options.formData, function (index, field) {
                                $('<input type="hidden"/>')
                                    .prop('name', field.name)
                                    .val(field.value)
                                    .appendTo(form);
                            });
                        }
                        if (options.fileInput && options.fileInput.length &&
                                options.type === 'POST') {
                            fileInputClones = options.fileInput.clone();
                            // Insert a clone for each file input field:
                            options.fileInput.after(function (index) {
                                return fileInputClones[index];
                            });
                            if (options.paramName) {
                                options.fileInput.each(function (index) {
                                    $(this).prop(
                                        'name',
                                        paramNames[index] || options.paramName
                                    );
                                });
                            }
                            // Appending the file input fields to the hidden form
                            // removes them from their original location:
                            form
                                .append(options.fileInput)
                                .prop('enctype', 'multipart/form-data')
                                // enctype must be set as encoding for IE:
                                .prop('encoding', 'multipart/form-data');
                            // Remove the HTML5 form attribute from the input(s):
                            options.fileInput.removeAttr('form');
                        }
                        form.submit();
                        // Insert the file input fields at their original location
                        // by replacing the clones with the originals:
                        if (fileInputClones && fileInputClones.length) {
                            options.fileInput.each(function (index, input) {
                                var clone = $(fileInputClones[index]);
                                // Restore the original name and form properties:
                                $(input)
                                    .prop('name', clone.prop('name'))
                                    .attr('form', clone.attr('form'));
                                clone.replaceWith(input);
                            });
                        }
                    });
                    form.append(iframe).appendTo(document.body);
                },
                abort: function () {
                    if (iframe) {
                        // javascript:false as iframe src aborts the request
                        // and prevents warning popups on HTTPS in IE6.
                        // concat is used to avoid the "Script URL" JSLint error:
                        iframe
                            .unbind('load')
                            .prop('src', initialIframeSrc);
                    }
                    if (form) {
                        form.remove();
                    }
                }
            };
        }
    });

    // The iframe transport returns the iframe content document as response.
    // The following adds converters from iframe to text, json, html, xml
    // and script.
    // Please note that the Content-Type for JSON responses has to be text/plain
    // or text/html, if the browser doesn't include application/json in the
    // Accept header, else IE will show a download dialog.
    // The Content-Type for XML responses on the other hand has to be always
    // application/xml or text/xml, so IE properly parses the XML response.
    // See also
    // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
    $.ajaxSetup({
        converters: {
            'iframe text': function (iframe) {
                return iframe && $(iframe[0].body).text();
            },
            'iframe json': function (iframe) {
                return iframe && $.parseJSON($(iframe[0].body).text());
            },
            'iframe html': function (iframe) {
                return iframe && $(iframe[0].body).html();
            },
            'iframe xml': function (iframe) {
                var xmlDoc = iframe && iframe[0];
                return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
                        $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
                            $(xmlDoc.body).html());
            },
            'iframe script': function (iframe) {
                return iframe && $.globalEval($(iframe[0].body).text());
            }
        }
    });

}));
;
jQuery.fn.liScroll = function(settings) {
		settings = jQuery.extend({
		travelocity: 0.07
		}, settings);		
		return this.each(function(){
				var $strip = jQuery(this);
				$strip.addClass("newsticker")
				var stripWidth = 0;
				var $mask = $strip.wrap("<div class='mask'></div>");
				var $tickercontainer = $strip.parent().wrap("<div class='tickercontainer'></div>");								
				var containerWidth = $strip.parent().parent().width();	//a.k.a. 'mask' width 	
				$strip.find("li").each(function(i){
				stripWidth += jQuery(this, i).width();
				});
				$strip.width(stripWidth);			
				var defTiming = stripWidth/settings.travelocity;
				var totalTravel = stripWidth+containerWidth;								
				function scrollnews(spazio, tempo){
				$strip.animate({left: '-='+ spazio}, tempo, "linear", function(){$strip.css("left", containerWidth); scrollnews(totalTravel, defTiming);});
				}
				scrollnews(totalTravel, defTiming);				
				$strip.hover(function(){
				jQuery(this).stop();
				},
				function(){
				var offset = jQuery(this).offset();
				var residualSpace = offset.left + stripWidth;
				var residualTime = residualSpace/settings.travelocity;
				scrollnews(residualSpace, residualTime);
				});			
		});	
};;
/*
    jQuery News Ticker is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 2 of the License.
 
    jQuery News Ticker is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with jQuery News Ticker.  If not, see <http://www.gnu.org/licenses/>.
*/
(function($){  
	$.fn.ticker = function(options) { 
		// Extend our default options with those provided.
		// Note that the first arg to extend is an empty object -
		// this is to keep from overriding our "defaults" object.
		var opts = $.extend({}, $.fn.ticker.defaults, options); 

		// check that the passed element is actually in the DOM
		if ($(this).length == 0) {
			if (window.console && window.console.log) {
				window.console.log('Element does not exist in DOM!');
			}
			else {
				alert('Element does not exist in DOM!');		
			}
			return false;
		}
		
		/* Get the id of the UL to get our news content from */
		var newsID = '#' + $(this).attr('id');

		/* Get the tag type - we will check this later to makde sure it is a UL tag */
		var tagType = $(this).get(0).tagName; 	

		return this.each(function() { 
			// get a unique id for this ticker
			var uniqID = getUniqID();
			
			/* Internal vars */
			var settings = {				
				position: 0,
				time: 0,
				distance: 0,
				newsArr: {},
				play: true,
				paused: false,
				contentLoaded: false,
				dom: {
					contentID: '#ticker-content-' + uniqID,
					titleID: '#ticker-title-' + uniqID,
					titleElem: '#ticker-title-' + uniqID + ' SPAN',
					tickerID : '#ticker-' + uniqID,
					wrapperID: '#ticker-wrapper-' + uniqID,
					revealID: '#ticker-swipe-' + uniqID,
					revealElem: '#ticker-swipe-' + uniqID + ' SPAN',
					controlsID: '#ticker-controls-' + uniqID,
					prevID: '#prev-' + uniqID,
					nextID: '#next-' + uniqID,
					playPauseID: '#play-pause-' + uniqID
				}
			};

			// if we are not using a UL, display an error message and stop any further execution
			if (tagType != 'UL' && tagType != 'OL' && opts.htmlFeed === true) {
				debugError('Cannot use <' + tagType.toLowerCase() + '> type of element for this plugin - must of type <ul> or <ol>');
				return false;
			}

			// set the ticker direction
			opts.direction == 'rtl' ? opts.direction = 'right' : opts.direction = 'left';
			
			// lets go...
			initialisePage();
			/* Function to get the size of an Object*/
			function countSize(obj) {
			    var size = 0, key;
			    for (key in obj) {
			        if (obj.hasOwnProperty(key)) size++;
			    }
			    return size;
			};

			function getUniqID() {
				var newDate = new Date;
				return newDate.getTime();			
			}
			
			/* Function for handling debug and error messages */ 
			function debugError(obj) {
				if (opts.debugMode) {
					if (window.console && window.console.log) {
						window.console.log(obj);
					}
					else {
						alert(obj);			
					}
				}
			}

			/* Function to setup the page */
			function initialisePage() {
				// process the content for this ticker
				processContent();
				
				// add our HTML structure for the ticker to the DOM
				$(newsID).wrap('<div id="' + settings.dom.wrapperID.replace('#', '') + '"></div>');
				
				// remove any current content inside this ticker
				$(settings.dom.wrapperID).children().remove();
				
				$(settings.dom.wrapperID).append('<div id="' + settings.dom.tickerID.replace('#', '') + '" class="ticker"><div id="' + settings.dom.titleID.replace('#', '') + '" class="ticker-title"><span><!-- --></span></div><p id="' + settings.dom.contentID.replace('#', '') + '" class="ticker-content"></p><div id="' + settings.dom.revealID.replace('#', '') + '" class="ticker-swipe"><span><!-- --></span></div></div>');
				$(settings.dom.wrapperID).removeClass('no-js').addClass('ticker-wrapper has-js ' + opts.direction);
				// hide the ticker
				$(settings.dom.tickerElem + ',' + settings.dom.contentID).hide();
				// add the controls to the DOM if required
				if (opts.controls) {
					// add related events - set functions to run on given event
					$(settings.dom.controlsID).live('click mouseover mousedown mouseout mouseup', function (e) {
						var button = e.target.id;
						if (e.type == 'click') {	
							switch (button) {
								case settings.dom.prevID.replace('#', ''):
									// show previous item
									settings.paused = true;
									$(settings.dom.playPauseID).addClass('paused');
									manualChangeContent('prev');
									break;
								case settings.dom.nextID.replace('#', ''):
									// show next item
									settings.paused = true;
									$(settings.dom.playPauseID).addClass('paused');
									manualChangeContent('next');
									break;
								case settings.dom.playPauseID.replace('#', ''):
									// play or pause the ticker
									if (settings.play == true) {
										settings.paused = true;
										$(settings.dom.playPauseID).addClass('paused');
										pauseTicker();
									}
									else {
										settings.paused = false;
										$(settings.dom.playPauseID).removeClass('paused');
										restartTicker();
									}
									break;
							}	
						}
						else if (e.type == 'mouseover' && $('#' + button).hasClass('controls')) {
							$('#' + button).addClass('over');
						}
						else if (e.type == 'mousedown' && $('#' + button).hasClass('controls')) {
							$('#' + button).addClass('down');
						}
						else if (e.type == 'mouseup' && $('#' + button).hasClass('controls')) {
							$('#' + button).removeClass('down');
						}
						else if (e.type == 'mouseout' && $('#' + button).hasClass('controls')) {
							$('#' + button).removeClass('over');
						}
					});
					// add controls HTML to DOM
					$(settings.dom.wrapperID).append('<ul id="' + settings.dom.controlsID.replace('#', '') + '" class="ticker-controls"><li id="' + settings.dom.playPauseID.replace('#', '') + '" class="jnt-play-pause controls"><a href=""><!-- --></a></li><li id="' + settings.dom.prevID.replace('#', '') + '" class="jnt-prev controls"><a href=""><!-- --></a></li><li id="' + settings.dom.nextID.replace('#', '') + '" class="jnt-next controls"><a href=""><!-- --></a></li></ul>');
				}
				if (opts.displayType != 'fade') {
                	// add mouse over on the content
               		$(settings.dom.contentID).mouseover(function () {
               			if (settings.paused == false) {
               				pauseTicker();
               			}
               		}).mouseout(function () {
               			if (settings.paused == false) {
               				restartTicker();
               			}
               		});
				}
				// we may have to wait for the ajax call to finish here
				if (!opts.ajaxFeed) {
					setupContentAndTriggerDisplay();
				}
			}

			/* Start to process the content for this ticker */
			function processContent() {
				// check to see if we need to load content
				if (settings.contentLoaded == false) {
					// construct content
					if (opts.ajaxFeed) {
						if (opts.feedType == 'xml') {							
							$.ajax({
								url: opts.feedUrl,
								cache: false,
								dataType: opts.feedType,
								async: true,
								success: function(data){
									count = 0;	
									// get the 'root' node
									for (var a = 0; a < data.childNodes.length; a++) {
										if (data.childNodes[a].nodeName == 'rss') {
											xmlContent = data.childNodes[a];
										}
									}
									// find the channel node
									for (var i = 0; i < xmlContent.childNodes.length; i++) {
										if (xmlContent.childNodes[i].nodeName == 'channel') {
											xmlChannel = xmlContent.childNodes[i];
										}		
									}
									// for each item create a link and add the article title as the link text
									for (var x = 0; x < xmlChannel.childNodes.length; x++) {
										if (xmlChannel.childNodes[x].nodeName == 'item') {
											xmlItems = xmlChannel.childNodes[x];
											var title, link = false;
											for (var y = 0; y < xmlItems.childNodes.length; y++) {
												if (xmlItems.childNodes[y].nodeName == 'title') {      												    
													title = xmlItems.childNodes[y].lastChild.nodeValue;
												}
												else if (xmlItems.childNodes[y].nodeName == 'link') {												    
													link = xmlItems.childNodes[y].lastChild.nodeValue; 
												}
												if ((title !== false && title != '') && link !== false) {
												    settings.newsArr['item-' + count] = { type: opts.titleText, content: '<a href="' + link + '">' + title + '</a>' };												    count++;												    title = false;												    link = false;
												}
											}	
										}		
									}			
									// quick check here to see if we actually have any content - log error if not
									if (countSize(settings.newsArr < 1)) {
										debugError('Couldn\'t find any content from the XML feed for the ticker to use!');
										return false;
									}
									settings.contentLoaded = true;
									setupContentAndTriggerDisplay();
								}
							});							
						}
						else {
							debugError('Code Me!');	
						}						
					}
					else if (opts.htmlFeed) { 
						if($(newsID + ' LI').length > 0) {
							$(newsID + ' LI').each(function (i) {
								// maybe this could be one whole object and not an array of objects?
								settings.newsArr['item-' + i] = { type: opts.titleText, content: $(this).html()};
							});		
						}	
						else {
							debugError('Couldn\'t find HTML any content for the ticker to use!');
							return false;
						}
					}
					else {
						debugError('The ticker is set to not use any types of content! Check the settings for the ticker.');
						return false;
					}					
				}			
			}

			function setupContentAndTriggerDisplay() {

				settings.contentLoaded = true;

				// update the ticker content with the correct item
				// insert news content into DOM
				$(settings.dom.titleElem).html(settings.newsArr['item-' + settings.position].type);
				$(settings.dom.contentID).html(settings.newsArr['item-' + settings.position].content);

				// set the next content item to be used - loop round if we are at the end of the content
				if (settings.position == (countSize(settings.newsArr) -1)) {
					settings.position = 0;
				}
				else {		
					settings.position++;
				}			

				// get the values of content and set the time of the reveal (so all reveals have the same speed regardless of content size)
				distance = $(settings.dom.contentID).width();
				time = distance / opts.speed;

				// start the ticker animation						
				revealContent();		
			}

			// slide back cover or fade in content
			function revealContent() {
				$(settings.dom.contentID).css('opacity', '1');
				if(settings.play) {	
					// get the width of the title element to offset the content and reveal	
					var offset = $(settings.dom.titleID).width() + 20;
	
					$(settings.dom.revealID).css(opts.direction, offset + 'px');
					// show the reveal element and start the animation
					if (opts.displayType == 'fade') {
						// fade in effect ticker
						$(settings.dom.revealID).hide(0, function () {
							$(settings.dom.contentID).css(opts.direction, offset + 'px').fadeIn(opts.fadeInSpeed, postReveal);
						});						
					}
					else if (opts.displayType == 'scroll') {
					    // to code
					}
					else {
						// default bbc scroll effect
						$(settings.dom.revealElem).show(0, function () {
							$(settings.dom.contentID).css(opts.direction, offset + 'px').show();
							// set our animation direction
							animationAction = opts.direction == 'right' ? { marginRight: distance + 'px'} : { marginLeft: distance + 'px' };
							$(settings.dom.revealID).css('margin-' + opts.direction, '0px').delay(20).animate(animationAction, time, 'linear', postReveal);
						});		
					}
				}
				else {
					return false;					
				}
			};

			// here we hide the current content and reset the ticker elements to a default state ready for the next ticker item
			function postReveal() {				
				if(settings.play) {		
					// we have to separately fade the content out here to get around an IE bug - needs further investigation
					$(settings.dom.contentID).delay(opts.pauseOnItems).fadeOut(opts.fadeOutSpeed);
					// deal with the rest of the content, prepare the DOM and trigger the next ticker
					if (opts.displayType == 'fade') {
						$(settings.dom.contentID).fadeOut(opts.fadeOutSpeed, function () {
							$(settings.dom.wrapperID)
								.find(settings.dom.revealElem + ',' + settings.dom.contentID)
									.hide()
								.end().find(settings.dom.tickerID + ',' + settings.dom.revealID)
									.show()
								.end().find(settings.dom.tickerID + ',' + settings.dom.revealID)
									.removeAttr('style');								
							setupContentAndTriggerDisplay();						
						});
					}
					else {
						$(settings.dom.revealID).hide(0, function () {
							$(settings.dom.contentID).fadeOut(opts.fadeOutSpeed, function () {
								$(settings.dom.wrapperID)
									.find(settings.dom.revealElem + ',' + settings.dom.contentID)
										.hide()
									.end().find(settings.dom.tickerID + ',' + settings.dom.revealID)
										.show()
									.end().find(settings.dom.tickerID + ',' + settings.dom.revealID)
										.removeAttr('style');								
								setupContentAndTriggerDisplay();						
							});
						});	
					}
				}
				else {
					$(settings.dom.revealElem).hide();
				}
			}

			// pause ticker
			function pauseTicker() {				
				settings.play = false;
				// stop animation and show content - must pass "true, true" to the stop function, or we can get some funky behaviour
				$(settings.dom.tickerID + ',' + settings.dom.revealID + ',' + settings.dom.titleID + ',' + settings.dom.titleElem + ',' + settings.dom.revealElem + ',' + settings.dom.contentID).stop(true, true);
				$(settings.dom.revealID + ',' + settings.dom.revealElem).hide();
				$(settings.dom.wrapperID)
					.find(settings.dom.titleID + ',' + settings.dom.titleElem).show()
						.end().find(settings.dom.contentID).show();
			}

			// play ticker
			function restartTicker() {				
				settings.play = true;
				settings.paused = false;
				// start the ticker again
				postReveal();	
			}

			// change the content on user input
			function manualChangeContent(direction) {
				pauseTicker();
				switch (direction) {
					case 'prev':
						if (settings.position == 0) {
							settings.position = countSize(settings.newsArr) -2;
						}
						else if (settings.position == 1) {
							settings.position = countSize(settings.newsArr) -1;
						}
						else {
							settings.position = settings.position - 2;
						}
						$(settings.dom.titleElem).html(settings.newsArr['item-' + settings.position].type);
						$(settings.dom.contentID).html(settings.newsArr['item-' + settings.position].content);						
						break;
					case 'next':
						$(settings.dom.titleElem).html(settings.newsArr['item-' + settings.position].type);
						$(settings.dom.contentID).html(settings.newsArr['item-' + settings.position].content);
						break;
				}
				// set the next content item to be used - loop round if we are at the end of the content
				if (settings.position == (countSize(settings.newsArr) -1)) {
					settings.position = 0;
				}
				else {		
					settings.position++;
				}	
			}
		});  
	};  

	// plugin defaults - added as a property on our plugin function
	$.fn.ticker.defaults = {
		speed: 0.10,			
		ajaxFeed: false,
		feedUrl: '',
		feedType: 'xml',
		displayType: 'reveal',
		htmlFeed: true,
		debugMode: true,
		controls: true,
		titleText: 'Latest',	
		direction: 'ltr',	
		pauseOnItems: 3000,
		fadeInSpeed: 600,
		fadeOutSpeed: 300
	};	
})(jQuery);;
/*
 * jQuery Bootstrap News Box v1.0.1
 * 
 * Copyright 2014, Dragan Mitrovic
 * email: gagi270683@gmail.com
 * Free to use and abuse under the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 */

//Utility
if (typeof Object.create !== 'function') {
    //Douglas Crockford inheritance function
    Object.create = function (obj) {
        function F() { };
        F.prototype = obj;
        return new F();
    };
}

(function ($, w, d, undefined) {

    var NewsBox = {

        init: function ( options, elem ) {
            //cache the references
            var self = this;
            self.elem = elem;
            self.$elem = $( elem );
            
            self.newsTagName = self.$elem.find(":first-child").prop('tagName');
            self.newsClassName = self.$elem.find(":first-child").attr('class');

            self.timer = null;
            self.resizeTimer = null; // used with window.resize event 
            self.animationStarted = false;
            self.isHovered = false;


            if ( typeof options === 'string' ) {
                //string was passed
                if(console) {
                    console.error("String property override is not supported");
                }
                throw ("String property override is not supported");
            } else {
                //object was passed
                //extend user options overrides
                self.options = $.extend( {}, $.fn.bootstrapNews.options, options );
                
                self.prepareLayout();


                //autostart animation
                if(self.options.autoplay) {
                    self.animate();
                }

                if ( self.options.navigation ) {
                    self.buildNavigation();
                }

                //enable users to override the methods
                if( typeof self.options.onToDo === 'function') {
                    self.options.onToDo.apply(self, arguments);
                }

            }
        },

        prepareLayout: function() {
            var self = this;

            //checking mouse position
                
            $(self.elem).find('.'+self.newsClassName).on('mouseenter', function(){
                self.onReset(true);
            });

            $(self.elem).find('.'+self.newsClassName).on('mouseout', function(){
                self.onReset(false);
            });

            //set news visible / hidden
            $.map(self.$elem.find(self.newsTagName), function(newsItem, index){
                if(index > self.options.newsPerPage - 1) {
                    $(newsItem).hide();
                } else {
                    $(newsItem).show();
                }
            });

            //prevent user to select more news that it actualy have

            if( self.$elem.find(self.newsTagName).length < self.options.newsPerPage ) {
                self.options.newsPerPage = self.$elem.find(self.newsTagName).length;
            }
            
            //get height of the very first self.options.newsPerPage news
            var height = 0;

            $.map(self.$elem.find(self.newsTagName), function( newsItem, index ) {
                if ( index < self.options.newsPerPage ) {
                    height = parseInt(height) + parseInt($(newsItem).height()) + 10;
                }
            });

            $(self.elem).css({"overflow-y": "hidden", "height": height});

            //recalculate news box height for responsive interfaces
            $( w ).resize(function() {
                if ( self.resizeTimer !== null ) {
                    clearTimeout( self.resizeTimer );
                }
                self.resizeTimer = setTimeout( function() {
                  self.prepareLayout();
                }, 200 );
            });

        },

        findPanelObject: function() { 
            var panel = this.$elem;

            while ( panel.parent() !== undefined ) {
                panel = panel.parent();
                if ( panel.parent().hasClass('panel') ) { 
                    return panel.parent();
                }
            }

            return undefined;
        },

        buildNavigation: function() { 
            var panel = this.findPanelObject();
            if( panel ) {
                var nav = '<ul class="pagination pull-right" style="margin: 0px;">' +
                             '<li><a href="#" class="prev"><span class="glyphicon glyphicon-chevron-down"></span></a></li>' +
                             '<li><a href="#" class="next"><span class="glyphicon glyphicon-chevron-up"></span></a></li>' +
                           '</ul><div class="clearfix"></div>';


                var footer = $(panel).find(".panel-footer")[0];
                if( footer ) {
                    $(footer).append(nav);
                } else {
                    $(panel).append('<div class="panel-footer">' + nav + '</div>');
                }

                var self = this;
                $(panel).find('.prev').on('click', function(ev){
                    ev.preventDefault();
                    self.onPrev();
                });

                $(panel).find('.next').on('click', function(ev){
                    ev.preventDefault();
                    self.onNext();
                });

            }
        },

        onStop: function() {
            
        },

        onPause: function() {
            var self = this;
            self.isHovered = true;
            if(this.options.autoplay && self.timer) {
                clearTimeout(self.timer);
            }
        },

        onReset: function(status) {
            var self = this;
            if(self.timer) {
                clearTimeout(self.timer);
            }

            if(self.options.autoplay) {
                self.isHovered = status;
                self.animate();
            }
        },

        animate: function() {
            var self = this;
            self.timer = setTimeout(function() {
                
                if ( !self.options.pauseOnHover ) {
                    self.isHovered = false;
                }

                if (! self.isHovered) {
                     if(self.options.direction === 'up') {
                        self.onNext();
                     } else {
                        self.onPrev();
                     }
                } 
            }, self.options.newsTickerInterval);
        },

        onPrev: function() {
            
            var self = this;

            if ( self.animationStarted ) {
                return false;
            }

            self.animationStarted = true;

            var html = '<' + self.newsTagName + ' style="display:none;" class="' + self.newsClassName + '">' + $(self.$elem).find(self.newsTagName).last().html() + '</' + self.newsTagName + '>';
            $(self.$elem).prepend(html);
            $(self.$elem).find(self.newsTagName).first().slideDown(self.options.animationSpeed, function(){
                $(self.$elem).find(self.newsTagName).last().remove();
            });

            $(self.$elem).find(self.newsTagName +':nth-child(' + parseInt(self.options.newsPerPage + 1) + ')').slideUp(self.options.animationSpeed, function(){
                self.animationStarted = false;
                self.onReset(self.isHovered);
            });

            $(self.elem).find('.'+self.newsClassName).on('mouseenter', function(){
                self.onReset(true);
            });

            $(self.elem).find('.'+self.newsClassName).on('mouseout', function(){
                self.onReset(false);
            });
        },

        onNext: function() {
            var self = this;

            if ( self.animationStarted ) {
                return false;
            }

            self.animationStarted = true;

            var html = '<' + self.newsTagName + ' style="display:none;" class=' + self.newsClassName + '>' + $(self.$elem).find(self.newsTagName).first().html() + '</' + self.newsTagName + '>';
            $(self.$elem).append(html);

            $(self.$elem).find(self.newsTagName).first().slideUp(self.options.animationSpeed, function(){
                $(this).remove();
            });

            $(self.$elem).find(self.newsTagName +':nth-child(' + parseInt(self.options.newsPerPage + 1) + ')').slideDown(self.options.animationSpeed, function(){
                self.animationStarted = false;
                self.onReset(self.isHovered);
            });

            $(self.elem).find('.'+self.newsClassName).on('mouseenter', function(){
                self.onReset(true);
            });

            $(self.elem).find('.'+self.newsClassName).on('mouseout', function(){
                self.onReset(false);
            });
        }
    };

    $.fn.bootstrapNews = function ( options ) {
        //enable multiple DOM object selection (class selector) + enable chaining like $(".class").bootstrapNews().chainingMethod()
        return this.each( function () {

            var newsBox = Object.create( NewsBox );

            newsBox.init( options, this );
            //console.log(newsBox);

        });
    };

    $.fn.bootstrapNews.options = {
        newsPerPage: 4,
        navigation: true,
        autoplay: true,
        direction:'up',
        animationSpeed: 'normal',
        newsTickerInterval: 4000, //4 secs
        pauseOnHover: true,
        onStop: null,
        onPause: null,
        onReset: null,
        onPrev: null,
        onNext: null,
        onToDo: null
    };

})(jQuery, window, document);;
/* =========================================================
 * bootstrap-treeview.js v1.2.0
 * =========================================================
 * Copyright 2013 Jonathan Miles
 * Project URL : http://www.jondmiles.com/bootstrap-treeview
 *
 * 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 ($, window, document, undefined) {

	/*global jQuery, console*/

	'use strict';

	var pluginName = 'treeview';

	var _default = {};

	_default.settings = {

		injectStyle: true,

		levels: 2,

		expandIcon: 'glyphicon glyphicon-plus',
		collapseIcon: 'glyphicon glyphicon-minus',
		emptyIcon: 'glyphicon',
		nodeIcon: '',
		selectedIcon: '',
		checkedIcon: 'glyphicon glyphicon-check',
		uncheckedIcon: 'glyphicon glyphicon-unchecked',

		color: undefined, // '#000000',
		backColor: undefined, // '#FFFFFF',
		borderColor: undefined, // '#dddddd',
		onhoverColor: '#F5F5F5',
		selectedColor: '#FFFFFF',
		selectedBackColor: '#428bca',
		searchResultColor: '#D9534F',
		searchResultBackColor: undefined, //'#FFFFFF',

		enableLinks: false,
		highlightSelected: true,
		highlightSearchResults: true,
		showBorder: true,
		showIcon: true,
		showCheckbox: false,
		showTags: false,
		multiSelect: false,

		// Event handlers
		onNodeChecked: undefined,
		onNodeCollapsed: undefined,
		onNodeDisabled: undefined,
		onNodeEnabled: undefined,
		onNodeExpanded: undefined,
		onNodeSelected: undefined,
		onNodeUnchecked: undefined,
		onNodeUnselected: undefined,
		onSearchComplete: undefined,
		onSearchCleared: undefined
	};

	_default.options = {
		silent: false,
		ignoreChildren: false
	};

	_default.searchOptions = {
		ignoreCase: true,
		exactMatch: false,
		revealResults: true
	};

	var Tree = function (element, options) {

		this.$element = $(element);
		this.elementId = element.id;
		this.styleId = this.elementId + '-style';

		this.init(options);

		return {

			// Options (public access)
			options: this.options,

			// Initialize / destroy methods
			init: $.proxy(this.init, this),
			remove: $.proxy(this.remove, this),

			// Get methods
			getNode: $.proxy(this.getNode, this),
			getParent: $.proxy(this.getParent, this),
			getSiblings: $.proxy(this.getSiblings, this),
			getSelected: $.proxy(this.getSelected, this),
			getUnselected: $.proxy(this.getUnselected, this),
			getExpanded: $.proxy(this.getExpanded, this),
			getCollapsed: $.proxy(this.getCollapsed, this),
			getChecked: $.proxy(this.getChecked, this),
			getUnchecked: $.proxy(this.getUnchecked, this),
			getDisabled: $.proxy(this.getDisabled, this),
			getEnabled: $.proxy(this.getEnabled, this),

			// Select methods
			selectNode: $.proxy(this.selectNode, this),
			unselectNode: $.proxy(this.unselectNode, this),
			toggleNodeSelected: $.proxy(this.toggleNodeSelected, this),

			// Expand / collapse methods
			collapseAll: $.proxy(this.collapseAll, this),
			collapseNode: $.proxy(this.collapseNode, this),
			expandAll: $.proxy(this.expandAll, this),
			expandNode: $.proxy(this.expandNode, this),
			toggleNodeExpanded: $.proxy(this.toggleNodeExpanded, this),
			revealNode: $.proxy(this.revealNode, this),

			// Expand / collapse methods
			checkAll: $.proxy(this.checkAll, this),
			checkNode: $.proxy(this.checkNode, this),
			uncheckAll: $.proxy(this.uncheckAll, this),
			uncheckNode: $.proxy(this.uncheckNode, this),
			toggleNodeChecked: $.proxy(this.toggleNodeChecked, this),

			// Disable / enable methods
			disableAll: $.proxy(this.disableAll, this),
			disableNode: $.proxy(this.disableNode, this),
			enableAll: $.proxy(this.enableAll, this),
			enableNode: $.proxy(this.enableNode, this),
			toggleNodeDisabled: $.proxy(this.toggleNodeDisabled, this),

			// Search methods
			search: $.proxy(this.search, this),
			clearSearch: $.proxy(this.clearSearch, this)
		};
	};

	Tree.prototype.init = function (options) {

		this.tree = [];
		this.nodes = [];

		if (options.data) {
			if (typeof options.data === 'string') {
				options.data = $.parseJSON(options.data);
			}
			this.tree = $.extend(true, [], options.data);
			delete options.data;
		}
		this.options = $.extend({}, _default.settings, options);

		this.destroy();
		this.subscribeEvents();
		this.setInitialStates({ nodes: this.tree }, 0);
		this.render();
	};

	Tree.prototype.remove = function () {
		this.destroy();
		$.removeData(this, pluginName);
		$('#' + this.styleId).remove();
	};

	Tree.prototype.destroy = function () {

		if (!this.initialized) return;

		this.$wrapper.remove();
		this.$wrapper = null;

		// Switch off events
		this.unsubscribeEvents();

		// Reset this.initialized flag
		this.initialized = false;
	};

	Tree.prototype.unsubscribeEvents = function () {

		this.$element.off('click');
		this.$element.off('nodeChecked');
		this.$element.off('nodeCollapsed');
		this.$element.off('nodeDisabled');
		this.$element.off('nodeEnabled');
		this.$element.off('nodeExpanded');
		this.$element.off('nodeSelected');
		this.$element.off('nodeUnchecked');
		this.$element.off('nodeUnselected');
		this.$element.off('searchComplete');
		this.$element.off('searchCleared');
	};

	Tree.prototype.subscribeEvents = function () {

		this.unsubscribeEvents();

		this.$element.on('click', $.proxy(this.clickHandler, this));

		if (typeof (this.options.onNodeChecked) === 'function') {
			this.$element.on('nodeChecked', this.options.onNodeChecked);
		}

		if (typeof (this.options.onNodeCollapsed) === 'function') {
			this.$element.on('nodeCollapsed', this.options.onNodeCollapsed);
		}

		if (typeof (this.options.onNodeDisabled) === 'function') {
			this.$element.on('nodeDisabled', this.options.onNodeDisabled);
		}

		if (typeof (this.options.onNodeEnabled) === 'function') {
			this.$element.on('nodeEnabled', this.options.onNodeEnabled);
		}

		if (typeof (this.options.onNodeExpanded) === 'function') {
			this.$element.on('nodeExpanded', this.options.onNodeExpanded);
		}

		if (typeof (this.options.onNodeSelected) === 'function') {
			this.$element.on('nodeSelected', this.options.onNodeSelected);
		}

		if (typeof (this.options.onNodeUnchecked) === 'function') {
			this.$element.on('nodeUnchecked', this.options.onNodeUnchecked);
		}

		if (typeof (this.options.onNodeUnselected) === 'function') {
			this.$element.on('nodeUnselected', this.options.onNodeUnselected);
		}

		if (typeof (this.options.onSearchComplete) === 'function') {
			this.$element.on('searchComplete', this.options.onSearchComplete);
		}

		if (typeof (this.options.onSearchCleared) === 'function') {
			this.$element.on('searchCleared', this.options.onSearchCleared);
		}
	};

	/*
		Recurse the tree structure and ensure all nodes have
		valid initial states.  User defined states will be preserved.
		For performance we also take this opportunity to
		index nodes in a flattened structure
	*/
	Tree.prototype.setInitialStates = function (node, level) {

		if (!node.nodes) return;
		level += 1;

		var parent = node;
		var _this = this;
		$.each(node.nodes, function checkStates(index, node) {

			// nodeId : unique, incremental identifier
			node.nodeId = _this.nodes.length;

			// parentId : transversing up the tree
			node.parentId = parent.nodeId;

			// if not provided set selectable default value
			if (!node.hasOwnProperty('selectable')) {
				node.selectable = true;
			}

			// where provided we should preserve states
			node.state = node.state || {};

			// set checked state; unless set always false
			if (!node.state.hasOwnProperty('checked')) {
				node.state.checked = false;
			}

			// set enabled state; unless set always false
			if (!node.state.hasOwnProperty('disabled')) {
				node.state.disabled = false;
			}

			// set expanded state; if not provided based on levels
			if (!node.state.hasOwnProperty('expanded')) {
				if (!node.state.disabled &&
						(level < _this.options.levels) &&
						(node.nodes && node.nodes.length > 0)) {
					node.state.expanded = true;
				}
				else {
					node.state.expanded = false;
				}
			}

			// set selected state; unless set always false
			if (!node.state.hasOwnProperty('selected')) {
				node.state.selected = false;
			}

			// index nodes in a flattened structure for use later
			_this.nodes.push(node);

			// recurse child nodes and transverse the tree
			if (node.nodes) {
				_this.setInitialStates(node, level);
			}
		});
	};

	Tree.prototype.clickHandler = function (event) {

		if (!this.options.enableLinks) event.preventDefault();

		var target = $(event.target);
		var node = this.findNode(target);
		if (!node || node.state.disabled) return;
		
		var classList = target.attr('class') ? target.attr('class').split(' ') : [];
		if ((classList.indexOf('expand-icon') !== -1)) {

			this.toggleExpandedState(node, _default.options);
			this.render();
		}
		else if ((classList.indexOf('check-icon') !== -1)) {
			
			this.toggleCheckedState(node, _default.options);
			this.render();
		}
		else {
			
			if (node.selectable) {
				this.toggleSelectedState(node, _default.options);
			} else {
				this.toggleExpandedState(node, _default.options);
			}

			this.render();
		}
	};

	// Looks up the DOM for the closest parent list item to retrieve the
	// data attribute nodeid, which is used to lookup the node in the flattened structure.
	Tree.prototype.findNode = function (target) {

		var nodeId = target.closest('li.list-group-item').attr('data-nodeid');
		var node = this.nodes[nodeId];

		if (!node) {
			console.log('Error: node does not exist');
		}
		return node;
	};

	Tree.prototype.toggleExpandedState = function (node, options) {
		if (!node) return;
		this.setExpandedState(node, !node.state.expanded, options);
	};

	Tree.prototype.setExpandedState = function (node, state, options) {

		if (state === node.state.expanded) return;

		if (state && node.nodes) {

			// Expand a node
			node.state.expanded = true;
			if (!options.silent) {
				this.$element.trigger('nodeExpanded', $.extend(true, {}, node));
			}
		}
		else if (!state) {

			// Collapse a node
			node.state.expanded = false;
			if (!options.silent) {
				this.$element.trigger('nodeCollapsed', $.extend(true, {}, node));
			}

			// Collapse child nodes
			if (node.nodes && !options.ignoreChildren) {
				$.each(node.nodes, $.proxy(function (index, node) {
					this.setExpandedState(node, false, options);
				}, this));
			}
		}
	};

	Tree.prototype.toggleSelectedState = function (node, options) {
		if (!node) return;
		this.setSelectedState(node, !node.state.selected, options);
	};

	Tree.prototype.setSelectedState = function (node, state, options) {

		if (state === node.state.selected) return;

		if (state) {

			// If multiSelect false, unselect previously selected
			if (!this.options.multiSelect) {
				$.each(this.findNodes('true', 'g', 'state.selected'), $.proxy(function (index, node) {
					this.setSelectedState(node, false, options);
				}, this));
			}

			// Continue selecting node
			node.state.selected = true;
			if (!options.silent) {
				this.$element.trigger('nodeSelected', $.extend(true, {}, node));
			}
		}
		else {

			// Unselect node
			node.state.selected = false;
			if (!options.silent) {
				this.$element.trigger('nodeUnselected', $.extend(true, {}, node));
			}
		}
	};

	Tree.prototype.toggleCheckedState = function (node, options) {
		if (!node) return;
		this.setCheckedState(node, !node.state.checked, options);
	};

	Tree.prototype.setCheckedState = function (node, state, options) {

		if (state === node.state.checked) return;

		if (state) {

			// Check node
			node.state.checked = true;

			if (!options.silent) {
				this.$element.trigger('nodeChecked', $.extend(true, {}, node));
			}
		}
		else {

			// Uncheck node
			node.state.checked = false;
			if (!options.silent) {
				this.$element.trigger('nodeUnchecked', $.extend(true, {}, node));
			}
		}
	};

	Tree.prototype.setDisabledState = function (node, state, options) {

		if (state === node.state.disabled) return;

		if (state) {

			// Disable node
			node.state.disabled = true;

			// Disable all other states
			this.setExpandedState(node, false, options);
			this.setSelectedState(node, false, options);
			this.setCheckedState(node, false, options);

			if (!options.silent) {
				this.$element.trigger('nodeDisabled', $.extend(true, {}, node));
			}
		}
		else {

			// Enabled node
			node.state.disabled = false;
			if (!options.silent) {
				this.$element.trigger('nodeEnabled', $.extend(true, {}, node));
			}
		}
	};

	Tree.prototype.render = function () {

		if (!this.initialized) {

			// Setup first time only components
			this.$element.addClass(pluginName);
			this.$wrapper = $(this.template.list);

			this.injectStyle();

			this.initialized = true;
		}

		this.$element.empty().append(this.$wrapper.empty());

		// Build tree
		this.buildTree(this.tree, 0);
	};

	// Starting from the root node, and recursing down the
	// structure we build the tree one node at a time
	Tree.prototype.buildTree = function (nodes, level) {

		if (!nodes) return;
		level += 1;

		var _this = this;
		$.each(nodes, function addNodes(id, node) {

			var treeItem = $(_this.template.item)
				.addClass('node-' + _this.elementId)
				.addClass(node.state.checked ? 'node-checked' : '')
				.addClass(node.state.disabled ? 'node-disabled': '')
				.addClass(node.state.selected ? 'node-selected' : '')
				.addClass(node.searchResult ? 'search-result' : '') 
				.attr('data-nodeid', node.nodeId)
				.attr('style', _this.buildStyleOverride(node));

			// Add indent/spacer to mimic tree structure
			for (var i = 0; i < (level - 1); i++) {
				treeItem.append(_this.template.indent);
			}

			// Add expand, collapse or empty spacer icons
			var classList = [];
			if (node.nodes) {
				classList.push('expand-icon');
				if (node.state.expanded) {
					classList.push(_this.options.collapseIcon);
				}
				else {
					classList.push(_this.options.expandIcon);
				}
			}
			else {
				classList.push(_this.options.emptyIcon);
			}

			treeItem
				.append($(_this.template.icon)
					.addClass(classList.join(' '))
				);


			// Add node icon
			if (_this.options.showIcon) {
				
				var classList = ['node-icon'];

				classList.push(node.icon || _this.options.nodeIcon);
				if (node.state.selected) {
					classList.pop();
					classList.push(node.selectedIcon || _this.options.selectedIcon || 
									node.icon || _this.options.nodeIcon);
				}

				treeItem
					.append($(_this.template.icon)
						.addClass(classList.join(' '))
					);
			}

			// Add check / unchecked icon
			if (_this.options.showCheckbox) {

				var classList = ['check-icon'];
				if (node.state.checked) {
					classList.push(_this.options.checkedIcon); 
				}
				else {
					classList.push(_this.options.uncheckedIcon);
				}

				treeItem
					.append($(_this.template.icon)
						.addClass(classList.join(' '))
					);
			}

			// Add text
			if (_this.options.enableLinks) {
				// Add hyperlink
				treeItem
					.append($(_this.template.link)
						.attr('href', node.href)
						.append(node.text)
					);
			}
			else {
				// otherwise just text
				treeItem
					.append(node.text);
			}

			// Add tags as badges
			if (_this.options.showTags && node.tags) {
				$.each(node.tags, function addTag(id, tag) {
					treeItem
						.append($(_this.template.badge)
							.append(tag)
						);
				});
			}

			// Add item to the tree
			_this.$wrapper.append(treeItem);

			// Recursively add child ndoes
			if (node.nodes && node.state.expanded && !node.state.disabled) {
				return _this.buildTree(node.nodes, level);
			}
		});
	};

	// Define any node level style override for
	// 1. selectedNode
	// 2. node|data assigned color overrides
	Tree.prototype.buildStyleOverride = function (node) {
        // -- removed the style override when disabled
		//if (node.state.disabled) return '';

		var color = node.color;
		var backColor = node.backColor;

		if (this.options.highlightSelected && node.state.selected) {
			if (this.options.selectedColor) {
				color = this.options.selectedColor;
			}
			if (this.options.selectedBackColor) {
				backColor = this.options.selectedBackColor;
			}
		}

		if (this.options.highlightSearchResults && node.searchResult && !node.state.disabled) {
			if (this.options.searchResultColor) {
				color = this.options.searchResultColor;
			}
			if (this.options.searchResultBackColor) {
				backColor = this.options.searchResultBackColor;
			}
		}

		return 'color:' + color +
			';background-color:' + backColor + ';';
	};

	// Add inline style into head
	Tree.prototype.injectStyle = function () {

		if (this.options.injectStyle && !document.getElementById(this.styleId)) {
			$('<style type="text/css" id="' + this.styleId + '"> ' + this.buildStyle() + ' </style>').appendTo('head');
		}
	};

	// Construct trees style based on user options
	Tree.prototype.buildStyle = function () {

		var style = '.node-' + this.elementId + '{';

		if (this.options.color) {
			style += 'color:' + this.options.color + ';';
		}

		if (this.options.backColor) {
			style += 'background-color:' + this.options.backColor + ';';
		}

		if (!this.options.showBorder) {
			style += 'border:none;';
		}
		else if (this.options.borderColor) {
			style += 'border:1px solid ' + this.options.borderColor + ';';
		}
		style += '}';

		if (this.options.onhoverColor) {
			style += '.node-' + this.elementId + ':not(.node-disabled):hover{' +
				'background-color:' + this.options.onhoverColor + ';' +
			'}';
		}

		return this.css + style;
	};

	Tree.prototype.template = {
		list: '<ul class="list-group"></ul>',
		item: '<li class="list-group-item"></li>',
		indent: '<span class="indent"></span>',
		icon: '<span class="icon"></span>',
		link: '<a href="#" style="color:inherit;"></a>',
		badge: '<span class="badge"></span>'
	};

	Tree.prototype.css = '.treeview .list-group-item{cursor:pointer}.treeview span.indent{margin-left:10px;margin-right:10px}.treeview span.icon{width:12px;margin-right:5px}.treeview .node-disabled{color:silver;cursor:not-allowed}'


	/**
		Returns a single node object that matches the given node id.
		@param {Number} nodeId - A node's unique identifier
		@return {Object} node - Matching node
	*/
	Tree.prototype.getNode = function (nodeId) {
		return this.nodes[nodeId];
	};

	/**
		Returns the parent node of a given node, if valid otherwise returns undefined.
		@param {Object|Number} identifier - A valid node or node id
		@returns {Object} node - The parent node
	*/
	Tree.prototype.getParent = function (identifier) {
		var node = this.identifyNode(identifier);
		return this.nodes[node.parentId];
	};

	/**
		Returns an array of sibling nodes for a given node, if valid otherwise returns undefined.
		@param {Object|Number} identifier - A valid node or node id
		@returns {Array} nodes - Sibling nodes
	*/
	Tree.prototype.getSiblings = function (identifier) {
		var node = this.identifyNode(identifier);
		var parent = this.getParent(node);
		var nodes = parent ? parent.nodes : this.tree;
		return nodes.filter(function (obj) {
				return obj.nodeId !== node.nodeId;
			});
	};

	/**
		Returns an array of selected nodes.
		@returns {Array} nodes - Selected nodes
	*/
	Tree.prototype.getSelected = function () {
		return this.findNodes('true', 'g', 'state.selected');
	};

	/**
		Returns an array of unselected nodes.
		@returns {Array} nodes - Unselected nodes
	*/
	Tree.prototype.getUnselected = function () {
		return this.findNodes('false', 'g', 'state.selected');
	};

	/**
		Returns an array of expanded nodes.
		@returns {Array} nodes - Expanded nodes
	*/
	Tree.prototype.getExpanded = function () {
		return this.findNodes('true', 'g', 'state.expanded');
	};

	/**
		Returns an array of collapsed nodes.
		@returns {Array} nodes - Collapsed nodes
	*/
	Tree.prototype.getCollapsed = function () {
		return this.findNodes('false', 'g', 'state.expanded');
	};

	/**
		Returns an array of checked nodes.
		@returns {Array} nodes - Checked nodes
	*/
	Tree.prototype.getChecked = function () {
		return this.findNodes('true', 'g', 'state.checked');
	};

	/**
		Returns an array of unchecked nodes.
		@returns {Array} nodes - Unchecked nodes
	*/
	Tree.prototype.getUnchecked = function () {
		return this.findNodes('false', 'g', 'state.checked');
	};

	/**
		Returns an array of disabled nodes.
		@returns {Array} nodes - Disabled nodes
	*/
	Tree.prototype.getDisabled = function () {
		return this.findNodes('true', 'g', 'state.disabled');
	};

	/**
		Returns an array of enabled nodes.
		@returns {Array} nodes - Enabled nodes
	*/
	Tree.prototype.getEnabled = function () {
		return this.findNodes('false', 'g', 'state.disabled');
	};


	/**
		Set a node state to selected
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.selectNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setSelectedState(node, true, options);
		}, this));

		this.render();
	};

	/**
		Set a node state to unselected
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.unselectNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setSelectedState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Toggles a node selected state; selecting if unselected, unselecting if selected.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.toggleNodeSelected = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.toggleSelectedState(node, options);
		}, this));

		this.render();
	};


	/**
		Collapse all tree nodes
		@param {optional Object} options
	*/
	Tree.prototype.collapseAll = function (options) {
		var identifiers = this.findNodes('true', 'g', 'state.expanded');
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setExpandedState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Collapse a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.collapseNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setExpandedState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Expand all tree nodes
		@param {optional Object} options
	*/
	Tree.prototype.expandAll = function (options) {
		options = $.extend({}, _default.options, options);

		if (options && options.levels) {
			this.expandLevels(this.tree, options.levels, options);
		}
		else {
			var identifiers = this.findNodes('false', 'g', 'state.expanded');
			this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
				this.setExpandedState(node, true, options);
			}, this));
		}

		this.render();
	};

	/**
		Expand a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.expandNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setExpandedState(node, true, options);
			if (node.nodes && (options && options.levels)) {
				this.expandLevels(node.nodes, options.levels-1, options);
			}
		}, this));

		this.render();
	};

	Tree.prototype.expandLevels = function (nodes, level, options) {
		options = $.extend({}, _default.options, options);

		$.each(nodes, $.proxy(function (index, node) {
			this.setExpandedState(node, (level > 0) ? true : false, options);
			if (node.nodes) {
				this.expandLevels(node.nodes, level-1, options);
			}
		}, this));
	};

	/**
		Reveals a given tree node, expanding the tree from node to root.
		@param {Object|Number|Array} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.revealNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			var parentNode = this.getParent(node);
			while (parentNode) {
				this.setExpandedState(parentNode, true, options);
				parentNode = this.getParent(parentNode);
			};
		}, this));

		this.render();
	};

	/**
		Toggles a nodes expanded state; collapsing if expanded, expanding if collapsed.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.toggleNodeExpanded = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.toggleExpandedState(node, options);
		}, this));
		
		this.render();
	};


	/**
		Check all tree nodes
		@param {optional Object} options
	*/
	Tree.prototype.checkAll = function (options) {
		var identifiers = this.findNodes('false', 'g', 'state.checked');
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setCheckedState(node, true, options);
		}, this));

		this.render();
	};

	/**
		Check a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.checkNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setCheckedState(node, true, options);
		}, this));

		this.render();
	};

	/**
		Uncheck all tree nodes
		@param {optional Object} options
	*/
	Tree.prototype.uncheckAll = function (options) {
		var identifiers = this.findNodes('true', 'g', 'state.checked');
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setCheckedState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Uncheck a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.uncheckNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setCheckedState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Toggles a nodes checked state; checking if unchecked, unchecking if checked.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.toggleNodeChecked = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.toggleCheckedState(node, options);
		}, this));

		this.render();
	};


	/**
		Disable all tree nodes
		@param {optional Object} options
	*/
	Tree.prototype.disableAll = function (options) {
		var identifiers = this.findNodes('false', 'g', 'state.disabled');
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setDisabledState(node, true, options);
		}, this));

		this.render();
	};

	/**
		Disable a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.disableNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setDisabledState(node, true, options);
		}, this));

		this.render();
	};

	/**
		Enable all tree nodes
		@param {optional Object} options
	*/
	Tree.prototype.enableAll = function (options) {
		var identifiers = this.findNodes('true', 'g', 'state.disabled');
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setDisabledState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Enable a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.enableNode = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setDisabledState(node, false, options);
		}, this));

		this.render();
	};

	/**
		Toggles a nodes disabled state; disabling is enabled, enabling if disabled.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
	Tree.prototype.toggleNodeDisabled = function (identifiers, options) {
		this.forEachIdentifier(identifiers, options, $.proxy(function (node, options) {
			this.setDisabledState(node, !node.state.disabled, options);
		}, this));

		this.render();
	};


	/**
		Common code for processing multiple identifiers
	*/
	Tree.prototype.forEachIdentifier = function (identifiers, options, callback) {

		options = $.extend({}, _default.options, options);

		if (!(identifiers instanceof Array)) {
			identifiers = [identifiers];
		}

		$.each(identifiers, $.proxy(function (index, identifier) {
			callback(this.identifyNode(identifier), options);
		}, this));	
	};

	/*
		Identifies a node from either a node id or object
	*/
	Tree.prototype.identifyNode = function (identifier) {
		return ((typeof identifier) === 'number') ?
						this.nodes[identifier] :
						identifier;
	};

	/**
		Searches the tree for nodes (text) that match given criteria
		@param {String} pattern - A given string to match against
		@param {optional Object} options - Search criteria options
		@return {Array} nodes - Matching nodes
	*/
	Tree.prototype.search = function (pattern, options) {
		options = $.extend({}, _default.searchOptions, options);

		this.clearSearch({ render: false });

		var results = [];
		if (pattern && pattern.length > 0) {

			if (options.exactMatch) {
				pattern = '^' + pattern + '$';
			}

			var modifier = 'g';
			if (options.ignoreCase) {
				modifier += 'i';
			}

			results = this.findNodes(pattern, modifier);

			// Add searchResult property to all matching nodes
			// This will be used to apply custom styles
			// and when identifying result to be cleared
			$.each(results, function (index, node) {
				node.searchResult = true;
			})
		}

		// If revealResults, then render is triggered from revealNode
		// otherwise we just call render.
		if (options.revealResults) {
			this.revealNode(results);
		}
		else {
			this.render();
		}

		this.$element.trigger('searchComplete', $.extend(true, {}, results));

		return results;
	};

	/**
		Clears previous search results
	*/
	Tree.prototype.clearSearch = function (options) {

		options = $.extend({}, { render: true }, options);

		var results = $.each(this.findNodes('true', 'g', 'searchResult'), function (index, node) {
			node.searchResult = false;
		});

		if (options.render) {
			this.render();	
		}
		
		this.$element.trigger('searchCleared', $.extend(true, {}, results));
	};

	/**
		Find nodes that match a given criteria
		@param {String} pattern - A given string to match against
		@param {optional String} modifier - Valid RegEx modifiers
		@param {optional String} attribute - Attribute to compare pattern against
		@return {Array} nodes - Nodes that match your criteria
	*/
	Tree.prototype.findNodes = function (pattern, modifier, attribute) {

		modifier = modifier || 'g';
		attribute = attribute || 'text';

		var _this = this;
		return $.grep(this.nodes, function (node) {
			var val = _this.getNodeValue(node, attribute);
			if (typeof val === 'string') {
				return val.match(new RegExp(pattern, modifier));
			}
		});
	};

	/**
		Recursive find for retrieving nested attributes values
		All values are return as strings, unless invalid
		@param {Object} obj - Typically a node, could be any object
		@param {String} attr - Identifies an object property using dot notation
		@return {String} value - Matching attributes string representation
	*/
	Tree.prototype.getNodeValue = function (obj, attr) {
		var index = attr.indexOf('.');
		if (index > 0) {
			var _obj = obj[attr.substring(0, index)];
			var _attr = attr.substring(index + 1, attr.length);
			return this.getNodeValue(_obj, _attr);
		}
		else {
			if (obj.hasOwnProperty(attr)) {
				return obj[attr].toString();
			}
			else {
				return undefined;
			}
		}
	};

	var logError = function (message) {
		if (window.console) {
			window.console.error(message);
		}
	};

	// Prevent against multiple instantiations,
	// handle updates and method calls
	$.fn[pluginName] = function (options, args) {

		var result;

		this.each(function () {
			var _this = $.data(this, pluginName);
			if (typeof options === 'string') {
				if (!_this) {
					logError('Not initialized, can not call method : ' + options);
				}
				else if (!$.isFunction(_this[options]) || options.charAt(0) === '_') {
					logError('No such method : ' + options);
				}
				else {
					if (!(args instanceof Array)) {
						args = [ args ];
					}
					result = _this[options].apply(_this, args);
				}
			}
			else if (typeof options === 'boolean') {
				result = _this;
			}
			else {
				$.data(this, pluginName, new Tree(this, $.extend(true, {}, options)));
			}
		});

		return result || this;
	};

})(jQuery, window, document);
;
/*
 *	jQuery dotdotdot 1.7.2
 *
 *	Copyright (c) Fred Heusschen
 *	www.frebsite.nl
 *
 *	Plugin website:
 *	dotdotdot.frebsite.nl
 *
 *	Licensed under the MIT license.
 *	http://en.wikipedia.org/wiki/MIT_License
 */
!function(t,e){function n(t,e,n){var r=t.children(),o=!1;t.empty();for(var i=0,d=r.length;d>i;i++){var l=r.eq(i);if(t.append(l),n&&t.append(n),a(t,e)){l.remove(),o=!0;break}n&&n.detach()}return o}function r(e,n,i,d,l){var s=!1,c="a table, thead, tbody, tfoot, tr, col, colgroup, object, embed, param, ol, ul, dl, blockquote, select, optgroup, option, textarea, script, style",u="script, .dotdotdot-keep";return e.contents().detach().each(function(){var f=this,h=t(f);if("undefined"==typeof f||3==f.nodeType&&0==t.trim(f.data).length)return!0;if(h.is(u))e.append(h);else{if(s)return!0;e.append(h),!l||h.is(d.after)||h.find(d.after).length||e[e.is(c)?"after":"append"](l),a(i,d)&&(s=3==f.nodeType?o(h,n,i,d,l):r(h,n,i,d,l),s||(h.detach(),s=!0)),s||l&&l.detach()}}),s}function o(e,n,r,o,d){var c=e[0];if(!c)return!1;var f=s(c),h=-1!==f.indexOf(" ")?" ":"　",p="letter"==o.wrap?"":h,g=f.split(p),v=-1,w=-1,b=0,y=g.length-1;for(o.fallbackToLetter&&0==b&&0==y&&(p="",g=f.split(p),y=g.length-1);y>=b&&(0!=b||0!=y);){var m=Math.floor((b+y)/2);if(m==w)break;w=m,l(c,g.slice(0,w+1).join(p)+o.ellipsis),a(r,o)?(y=w,o.fallbackToLetter&&0==b&&0==y&&(p="",g=g[0].split(p),v=-1,w=-1,b=0,y=g.length-1)):(v=w,b=w)}if(-1==v||1==g.length&&0==g[0].length){var x=e.parent();e.detach();var T=d&&d.closest(x).length?d.length:0;x.contents().length>T?c=u(x.contents().eq(-1-T),n):(c=u(x,n,!0),T||x.detach()),c&&(f=i(s(c),o),l(c,f),T&&d&&t(c).parent().append(d))}else f=i(g.slice(0,v+1).join(p),o),l(c,f);return!0}function a(t,e){return t.innerHeight()>e.maxHeight}function i(e,n){for(;t.inArray(e.slice(-1),n.lastCharacter.remove)>-1;)e=e.slice(0,-1);return t.inArray(e.slice(-1),n.lastCharacter.noEllipsis)<0&&(e+=n.ellipsis),e}function d(t){return{width:t.innerWidth(),height:t.innerHeight()}}function l(t,e){t.innerText?t.innerText=e:t.nodeValue?t.nodeValue=e:t.textContent&&(t.textContent=e)}function s(t){return t.innerText?t.innerText:t.nodeValue?t.nodeValue:t.textContent?t.textContent:""}function c(t){do t=t.previousSibling;while(t&&1!==t.nodeType&&3!==t.nodeType);return t}function u(e,n,r){var o,a=e&&e[0];if(a){if(!r){if(3===a.nodeType)return a;if(t.trim(e.text()))return u(e.contents().last(),n)}for(o=c(a);!o;){if(e=e.parent(),e.is(n)||!e.length)return!1;o=c(e[0])}if(o)return u(t(o),n)}return!1}function f(e,n){return e?"string"==typeof e?(e=t(e,n),e.length?e:!1):e.jquery?e:!1:!1}function h(t){for(var e=t.innerHeight(),n=["paddingTop","paddingBottom"],r=0,o=n.length;o>r;r++){var a=parseInt(t.css(n[r]),10);isNaN(a)&&(a=0),e-=a}return e}if(!t.fn.dotdotdot){t.fn.dotdotdot=function(e){if(0==this.length)return t.fn.dotdotdot.debug('No element found for "'+this.selector+'".'),this;if(this.length>1)return this.each(function(){t(this).dotdotdot(e)});var o=this;o.data("dotdotdot")&&o.trigger("destroy.dot"),o.data("dotdotdot-style",o.attr("style")||""),o.css("word-wrap","break-word"),"nowrap"===o.css("white-space")&&o.css("white-space","normal"),o.bind_events=function(){return o.bind("update.dot",function(e,d){e.preventDefault(),e.stopPropagation(),l.maxHeight="number"==typeof l.height?l.height:h(o),l.maxHeight+=l.tolerance,"undefined"!=typeof d&&(("string"==typeof d||d instanceof HTMLElement)&&(d=t("<div />").append(d).contents()),d instanceof t&&(i=d)),g=o.wrapInner('<div class="dotdotdot" />').children(),g.contents().detach().end().append(i.clone(!0)).find("br").replaceWith("  <br />  ").end().css({height:"auto",width:"auto",border:"none",padding:0,margin:0});var c=!1,u=!1;return s.afterElement&&(c=s.afterElement.clone(!0),c.show(),s.afterElement.detach()),a(g,l)&&(u="children"==l.wrap?n(g,l,c):r(g,o,g,l,c)),g.replaceWith(g.contents()),g=null,t.isFunction(l.callback)&&l.callback.call(o[0],u,i),s.isTruncated=u,u}).bind("isTruncated.dot",function(t,e){return t.preventDefault(),t.stopPropagation(),"function"==typeof e&&e.call(o[0],s.isTruncated),s.isTruncated}).bind("originalContent.dot",function(t,e){return t.preventDefault(),t.stopPropagation(),"function"==typeof e&&e.call(o[0],i),i}).bind("destroy.dot",function(t){t.preventDefault(),t.stopPropagation(),o.unwatch().unbind_events().contents().detach().end().append(i).attr("style",o.data("dotdotdot-style")||"").data("dotdotdot",!1)}),o},o.unbind_events=function(){return o.unbind(".dot"),o},o.watch=function(){if(o.unwatch(),"window"==l.watch){var e=t(window),n=e.width(),r=e.height();e.bind("resize.dot"+s.dotId,function(){n==e.width()&&r==e.height()&&l.windowResizeFix||(n=e.width(),r=e.height(),u&&clearInterval(u),u=setTimeout(function(){o.trigger("update.dot")},100))})}else c=d(o),u=setInterval(function(){if(o.is(":visible")){var t=d(o);(c.width!=t.width||c.height!=t.height)&&(o.trigger("update.dot"),c=t)}},500);return o},o.unwatch=function(){return t(window).unbind("resize.dot"+s.dotId),u&&clearInterval(u),o};var i=o.contents(),l=t.extend(!0,{},t.fn.dotdotdot.defaults,e),s={},c={},u=null,g=null;return l.lastCharacter.remove instanceof Array||(l.lastCharacter.remove=t.fn.dotdotdot.defaultArrays.lastCharacter.remove),l.lastCharacter.noEllipsis instanceof Array||(l.lastCharacter.noEllipsis=t.fn.dotdotdot.defaultArrays.lastCharacter.noEllipsis),s.afterElement=f(l.after,o),s.isTruncated=!1,s.dotId=p++,o.data("dotdotdot",!0).bind_events().trigger("update.dot"),l.watch&&o.watch(),o},t.fn.dotdotdot.defaults={ellipsis:"... ",wrap:"word",fallbackToLetter:!0,lastCharacter:{},tolerance:0,callback:null,after:null,height:null,watch:!1,windowResizeFix:!0},t.fn.dotdotdot.defaultArrays={lastCharacter:{remove:[" ","　",",",";",".","!","?"],noEllipsis:[]}},t.fn.dotdotdot.debug=function(){};var p=1,g=t.fn.html;t.fn.html=function(n){return n!=e&&!t.isFunction(n)&&this.data("dotdotdot")?this.trigger("update",[n]):g.apply(this,arguments)};var v=t.fn.text;t.fn.text=function(n){return n!=e&&!t.isFunction(n)&&this.data("dotdotdot")?(n=t("<div />").text(n).html(),this.trigger("update",[n])):v.apply(this,arguments)}}}(jQuery);;
var RadResponder = RadResponder || {};
RadResponder.Charts = RadResponder.Charts || {};

RadResponder.dataSeriesColors = [
	"#5B90BF",
	"#a3be8c",
	"#b48ead",
	"#bf616a",
	"#d08770",
	"#ebcb8b",
	"#96b5b4",
	"#8fa1b3",
    "#ab7967",
    "#373737",
    "#761799",
    "#5c6764",
    "#000000",
    "#FF0000",
    "#800000",
    "#FFFF00",
    "#808000",
    "#00FF00",
    "#008000",
    "#00FFFF",
    "#008080",
    "#0000FF",
    "#000080",
    "#FF00FF",
    "#800080",
    "#C0C0C0",
    "#808080"
];

RadResponder.getDataSeriesColorByDataPointType = function (dataPointTypeId) {
    var colors = RadResponder.dataSeriesColors;
    switch (dataPointTypeId) {
        case RadResponder.dataPointTypes.survey:
            return "#3D6AF2";
        case RadResponder.dataPointTypes.sample:
            return "#A0049A";
        case RadResponder.dataPointTypes.observation:
            return "#3EAD5E";
        case RadResponder.dataPointTypes.spectra:
            return "#DAD1FF";
        case RadResponder.dataPointTypes.set:
            return "#C1E4F7";
        case RadResponder.dataPointTypes.result:
            return colors[1];
        case RadResponder.dataPointTypes.aerialSurvey:
            return "#FFA3D8";
        case RadResponder.dataPointTypes.aerialReading:
            return "#90FFA2";
        case RadResponder.dataPointTypes.sitrep:
            return "#024034";
        case RadResponder.dataPointTypes.chemicalId:
            return "#FFFF55";
        case RadResponder.dataPointTypes.chemicalReading:
            return "#D93D4A";
        case RadResponder.dataPointTypes.chemicalSpectra:
            return "#1A258F";
        case RadResponder.dataPointTypes.colorimetricReading:
            return "#F135A7";
        case RadResponder.dataPointTypes.dose:
            return "#FF8E01";
        case RadResponder.dataPointTypes.accumulatedDose:
            return "#B28B00";
        case RadResponder.dataPointTypes.analysisRequest:
            return "#69656F";
        default:
            return colors[0];
    }
};

RadResponder.createHighlightColor = function(color, amt) {

    var usePound = false;

    if (color[0] === "#") {
        color = color.slice(1);
        usePound = true;
    }

    var num = parseInt(color, 16);

    var r = (num >> 16) + amt;

    if (r > 255) r = 255;
    else if (r < 0) r = 0;

    var b = ((num >> 8) & 0x00FF) + amt;

    if (b > 255) b = 255;
    else if (b < 0) b = 0;

    var g = (num & 0x0000FF) + amt;

    if (g > 255) g = 255;
    else if (g < 0) g = 0;

    return (usePound ? "#" : "") + (g | (b << 8) | (r << 16)).toString(16);
};

RadResponder.createTransparentHexColor = function(color, amt) {
    var usePound = false;
    if (color[0] === "#") {
        color = color.slice(1);
        usePound = true;
    }

    return (usePound ? "#" : "") + amt + color;
};

RadResponder.createTransparentRgbColor = function (color, amt) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    color = color.replace(shorthandRegex, function (m, r, g, b) {
        return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color);
    var rgb = result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;

    return 'rgba(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + amt + ')';
};;
/*!
* CreateJS
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

this.createjs = this.createjs||{};


//##############################################################################
// extend.js
//##############################################################################

/**
 * @class Utility Methods
 */

/**
 * Sets up the prototype chain and constructor property for a new class.
 *
 * This should be called right after creating the class constructor.
 *
 * 	function MySubClass() {}
 * 	createjs.extend(MySubClass, MySuperClass);
 * 	MySubClass.prototype.doSomething = function() { }
 *
 * 	var foo = new MySubClass();
 * 	console.log(foo instanceof MySuperClass); // true
 * 	console.log(foo.prototype.constructor === MySubClass); // true
 *
 * @method extend
 * @param {Function} subclass The subclass.
 * @param {Function} superclass The superclass to extend.
 * @return {Function} Returns the subclass's new prototype.
 */
createjs.extend = function(subclass, superclass) {
	"use strict";

	function o() { this.constructor = subclass; }
	o.prototype = superclass.prototype;
	return (subclass.prototype = new o());
};

//##############################################################################
// promote.js
//##############################################################################

/**
 * @class Utility Methods
 */

/**
 * Promotes any methods on the super class that were overridden, by creating an alias in the format `prefix_methodName`.
 * It is recommended to use the super class's name as the prefix.
 * An alias to the super class's constructor is always added in the format `prefix_constructor`.
 * This allows the subclass to call super class methods without using `function.call`, providing better performance.
 *
 * For example, if `MySubClass` extends `MySuperClass`, and both define a `draw` method, then calling `promote(MySubClass, "MySuperClass")`
 * would add a `MySuperClass_constructor` method to MySubClass and promote the `draw` method on `MySuperClass` to the
 * prototype of `MySubClass` as `MySuperClass_draw`.
 *
 * This should be called after the class's prototype is fully defined.
 *
 * 	function ClassA(name) {
 * 		this.name = name;
 * 	}
 * 	ClassA.prototype.greet = function() {
 * 		return "Hello "+this.name;
 * 	}
 *
 * 	function ClassB(name, punctuation) {
 * 		this.ClassA_constructor(name);
 * 		this.punctuation = punctuation;
 * 	}
 * 	createjs.extend(ClassB, ClassA);
 * 	ClassB.prototype.greet = function() {
 * 		return this.ClassA_greet()+this.punctuation;
 * 	}
 * 	createjs.promote(ClassB, "ClassA");
 *
 * 	var foo = new ClassB("World", "!?!");
 * 	console.log(foo.greet()); // Hello World!?!
 *
 * @method promote
 * @param {Function} subclass The class to promote super class methods on.
 * @param {String} prefix The prefix to add to the promoted method names. Usually the name of the superclass.
 * @return {Function} Returns the subclass.
 */
createjs.promote = function(subclass, prefix) {
	"use strict";

	var subP = subclass.prototype, supP = (Object.getPrototypeOf&&Object.getPrototypeOf(subP))||subP.__proto__;
	if (supP) {
		subP[(prefix+="_") + "constructor"] = supP.constructor; // constructor is not always innumerable
		for (var n in supP) {
			if (subP.hasOwnProperty(n) && (typeof supP[n] == "function")) { subP[prefix + n] = supP[n]; }
		}
	}
	return subclass;
};

//##############################################################################
// indexOf.js
//##############################################################################

/**
 * @class Utility Methods
 */

/**
 * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
 * that value.  Returns -1 if value is not found.
 *
 *      var i = createjs.indexOf(myArray, myElementToFind);
 *
 * @method indexOf
 * @param {Array} array Array to search for searchElement
 * @param searchElement Element to find in array.
 * @return {Number} The first index of searchElement in array.
 */
createjs.indexOf = function (array, searchElement){
	"use strict";

	for (var i = 0,l=array.length; i < l; i++) {
		if (searchElement === array[i]) {
			return i;
		}
	}
	return -1;
};

//##############################################################################
// Event.js
//##############################################################################

(function() {
	"use strict";

// constructor:
	/**
	 * Contains properties and methods shared by all events for use with
	 * {{#crossLink "EventDispatcher"}}{{/crossLink}}.
	 * 
	 * Note that Event objects are often reused, so you should never
	 * rely on an event object's state outside of the call stack it was received in.
	 * @class Event
	 * @param {String} type The event type.
	 * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
	 * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
	 * @constructor
	 **/
	function Event(type, bubbles, cancelable) {
		
	
	// public properties:
		/**
		 * The type of event.
		 * @property type
		 * @type String
		 **/
		this.type = type;
	
		/**
		 * The object that generated an event.
		 * @property target
		 * @type Object
		 * @default null
		 * @readonly
		*/
		this.target = null;
	
		/**
		 * The current target that a bubbling event is being dispatched from. For non-bubbling events, this will
		 * always be the same as target. For example, if childObj.parent = parentObj, and a bubbling event
		 * is generated from childObj, then a listener on parentObj would receive the event with
		 * target=childObj (the original target) and currentTarget=parentObj (where the listener was added).
		 * @property currentTarget
		 * @type Object
		 * @default null
		 * @readonly
		*/
		this.currentTarget = null;
	
		/**
		 * For bubbling events, this indicates the current event phase:<OL>
		 * 	<LI> capture phase: starting from the top parent to the target</LI>
		 * 	<LI> at target phase: currently being dispatched from the target</LI>
		 * 	<LI> bubbling phase: from the target to the top parent</LI>
		 * </OL>
		 * @property eventPhase
		 * @type Number
		 * @default 0
		 * @readonly
		*/
		this.eventPhase = 0;
	
		/**
		 * Indicates whether the event will bubble through the display list.
		 * @property bubbles
		 * @type Boolean
		 * @default false
		 * @readonly
		*/
		this.bubbles = !!bubbles;
	
		/**
		 * Indicates whether the default behaviour of this event can be cancelled via
		 * {{#crossLink "Event/preventDefault"}}{{/crossLink}}. This is set via the Event constructor.
		 * @property cancelable
		 * @type Boolean
		 * @default false
		 * @readonly
		*/
		this.cancelable = !!cancelable;
	
		/**
		 * The epoch time at which this event was created.
		 * @property timeStamp
		 * @type Number
		 * @default 0
		 * @readonly
		*/
		this.timeStamp = (new Date()).getTime();
	
		/**
		 * Indicates if {{#crossLink "Event/preventDefault"}}{{/crossLink}} has been called
		 * on this event.
		 * @property defaultPrevented
		 * @type Boolean
		 * @default false
		 * @readonly
		*/
		this.defaultPrevented = false;
	
		/**
		 * Indicates if {{#crossLink "Event/stopPropagation"}}{{/crossLink}} or
		 * {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called on this event.
		 * @property propagationStopped
		 * @type Boolean
		 * @default false
		 * @readonly
		*/
		this.propagationStopped = false;
	
		/**
		 * Indicates if {{#crossLink "Event/stopImmediatePropagation"}}{{/crossLink}} has been called
		 * on this event.
		 * @property immediatePropagationStopped
		 * @type Boolean
		 * @default false
		 * @readonly
		*/
		this.immediatePropagationStopped = false;
		
		/**
		 * Indicates if {{#crossLink "Event/remove"}}{{/crossLink}} has been called on this event.
		 * @property removed
		 * @type Boolean
		 * @default false
		 * @readonly
		*/
		this.removed = false;
	}
	var p = Event.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.

// public methods:
	/**
	 * Sets {{#crossLink "Event/defaultPrevented"}}{{/crossLink}} to true if the event is cancelable.
	 * Mirrors the DOM level 2 event standard. In general, cancelable events that have `preventDefault()` called will
	 * cancel the default behaviour associated with the event.
	 * @method preventDefault
	 **/
	p.preventDefault = function() {
		this.defaultPrevented = this.cancelable&&true;
	};

	/**
	 * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method stopPropagation
	 **/
	p.stopPropagation = function() {
		this.propagationStopped = true;
	};

	/**
	 * Sets {{#crossLink "Event/propagationStopped"}}{{/crossLink}} and
	 * {{#crossLink "Event/immediatePropagationStopped"}}{{/crossLink}} to true.
	 * Mirrors the DOM event standard.
	 * @method stopImmediatePropagation
	 **/
	p.stopImmediatePropagation = function() {
		this.immediatePropagationStopped = this.propagationStopped = true;
	};
	
	/**
	 * Causes the active listener to be removed via removeEventListener();
	 * 
	 * 		myBtn.addEventListener("click", function(evt) {
	 * 			// do stuff...
	 * 			evt.remove(); // removes this listener.
	 * 		});
	 * 
	 * @method remove
	 **/
	p.remove = function() {
		this.removed = true;
	};
	
	/**
	 * Returns a clone of the Event instance.
	 * @method clone
	 * @return {Event} a clone of the Event instance.
	 **/
	p.clone = function() {
		return new Event(this.type, this.bubbles, this.cancelable);
	};
	
	/**
	 * Provides a chainable shortcut method for setting a number of properties on the instance.
	 *
	 * @method set
	 * @param {Object} props A generic object containing properties to copy to the instance.
	 * @return {Event} Returns the instance the method is called on (useful for chaining calls.)
	 * @chainable
	*/
	p.set = function(props) {
		for (var n in props) { this[n] = props[n]; }
		return this;
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Event (type="+this.type+")]";
	};

	createjs.Event = Event;
}());

//##############################################################################
// EventDispatcher.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * EventDispatcher provides methods for managing queues of event listeners and dispatching events.
	 *
	 * You can either extend EventDispatcher or mix its methods into an existing prototype or instance by using the
	 * EventDispatcher {{#crossLink "EventDispatcher/initialize"}}{{/crossLink}} method.
	 * 
	 * Together with the CreateJS Event class, EventDispatcher provides an extended event model that is based on the
	 * DOM Level 2 event model, including addEventListener, removeEventListener, and dispatchEvent. It supports
	 * bubbling / capture, preventDefault, stopPropagation, stopImmediatePropagation, and handleEvent.
	 * 
	 * EventDispatcher also exposes a {{#crossLink "EventDispatcher/on"}}{{/crossLink}} method, which makes it easier
	 * to create scoped listeners, listeners that only run once, and listeners with associated arbitrary data. The 
	 * {{#crossLink "EventDispatcher/off"}}{{/crossLink}} method is merely an alias to
	 * {{#crossLink "EventDispatcher/removeEventListener"}}{{/crossLink}}.
	 * 
	 * Another addition to the DOM Level 2 model is the {{#crossLink "EventDispatcher/removeAllEventListeners"}}{{/crossLink}}
	 * method, which can be used to listeners for all events, or listeners for a specific event. The Event object also 
	 * includes a {{#crossLink "Event/remove"}}{{/crossLink}} method which removes the active listener.
	 *
	 * <h4>Example</h4>
	 * Add EventDispatcher capabilities to the "MyClass" class.
	 *
	 *      EventDispatcher.initialize(MyClass.prototype);
	 *
	 * Add an event (see {{#crossLink "EventDispatcher/addEventListener"}}{{/crossLink}}).
	 *
	 *      instance.addEventListener("eventName", handlerMethod);
	 *      function handlerMethod(event) {
	 *          console.log(event.target + " Was Clicked");
	 *      }
	 *
	 * <b>Maintaining proper scope</b><br />
	 * Scope (ie. "this") can be be a challenge with events. Using the {{#crossLink "EventDispatcher/on"}}{{/crossLink}}
	 * method to subscribe to events simplifies this.
	 *
	 *      instance.addEventListener("click", function(event) {
	 *          console.log(instance == this); // false, scope is ambiguous.
	 *      });
	 *      
	 *      instance.on("click", function(event) {
	 *          console.log(instance == this); // true, "on" uses dispatcher scope by default.
	 *      });
	 * 
	 * If you want to use addEventListener instead, you may want to use function.bind() or a similar proxy to manage
	 * scope.
	 *
	 * <b>Browser support</b>
	 * The event model in CreateJS can be used separately from the suite in any project, however the inheritance model
	 * requires modern browsers (IE9+).
	 *      
	 *
	 * @class EventDispatcher
	 * @constructor
	 **/
	function EventDispatcher() {
	
	
	// private properties:
		/**
		 * @protected
		 * @property _listeners
		 * @type Object
		 **/
		this._listeners = null;
		
		/**
		 * @protected
		 * @property _captureListeners
		 * @type Object
		 **/
		this._captureListeners = null;
	}
	var p = EventDispatcher.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// static public methods:
	/**
	 * Static initializer to mix EventDispatcher methods into a target object or prototype.
	 * 
	 * 		EventDispatcher.initialize(MyClass.prototype); // add to the prototype of the class
	 * 		EventDispatcher.initialize(myObject); // add to a specific instance
	 * 
	 * @method initialize
	 * @static
	 * @param {Object} target The target object to inject EventDispatcher methods into. This can be an instance or a
	 * prototype.
	 **/
	EventDispatcher.initialize = function(target) {
		target.addEventListener = p.addEventListener;
		target.on = p.on;
		target.removeEventListener = target.off =  p.removeEventListener;
		target.removeAllEventListeners = p.removeAllEventListeners;
		target.hasEventListener = p.hasEventListener;
		target.dispatchEvent = p.dispatchEvent;
		target._dispatchEvent = p._dispatchEvent;
		target.willTrigger = p.willTrigger;
	};
	

// public methods:
	/**
	 * Adds the specified event listener. Note that adding multiple listeners to the same function will result in
	 * multiple callbacks getting fired.
	 *
	 * <h4>Example</h4>
	 *
	 *      displayObject.addEventListener("click", handleClick);
	 *      function handleClick(event) {
	 *         // Click happened.
	 *      }
	 *
	 * @method addEventListener
	 * @param {String} type The string type of the event.
	 * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
	 * the event is dispatched.
	 * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
	 * @return {Function | Object} Returns the listener for chaining or assignment.
	 **/
	p.addEventListener = function(type, listener, useCapture) {
		var listeners;
		if (useCapture) {
			listeners = this._captureListeners = this._captureListeners||{};
		} else {
			listeners = this._listeners = this._listeners||{};
		}
		var arr = listeners[type];
		if (arr) { this.removeEventListener(type, listener, useCapture); }
		arr = listeners[type]; // remove may have deleted the array
		if (!arr) { listeners[type] = [listener];  }
		else { arr.push(listener); }
		return listener;
	};
	
	/**
	 * A shortcut method for using addEventListener that makes it easier to specify an execution scope, have a listener
	 * only run once, associate arbitrary data with the listener, and remove the listener.
	 * 
	 * This method works by creating an anonymous wrapper function and subscribing it with addEventListener.
	 * The wrapper function is returned for use with `removeEventListener` (or `off`).
	 * 
	 * <b>IMPORTANT:</b> To remove a listener added with `on`, you must pass in the returned wrapper function as the listener, or use
	 * {{#crossLink "Event/remove"}}{{/crossLink}}. Likewise, each time you call `on` a NEW wrapper function is subscribed, so multiple calls
	 * to `on` with the same params will create multiple listeners.
	 * 
	 * <h4>Example</h4>
	 * 
	 * 		var listener = myBtn.on("click", handleClick, null, false, {count:3});
	 * 		function handleClick(evt, data) {
	 * 			data.count -= 1;
	 * 			console.log(this == myBtn); // true - scope defaults to the dispatcher
	 * 			if (data.count == 0) {
	 * 				alert("clicked 3 times!");
	 * 				myBtn.off("click", listener);
	 * 				// alternately: evt.remove();
	 * 			}
	 * 		}
	 * 
	 * @method on
	 * @param {String} type The string type of the event.
	 * @param {Function | Object} listener An object with a handleEvent method, or a function that will be called when
	 * the event is dispatched.
	 * @param {Object} [scope] The scope to execute the listener in. Defaults to the dispatcher/currentTarget for function listeners, and to the listener itself for object listeners (ie. using handleEvent).
	 * @param {Boolean} [once=false] If true, the listener will remove itself after the first time it is triggered.
	 * @param {*} [data] Arbitrary data that will be included as the second parameter when the listener is called.
	 * @param {Boolean} [useCapture=false] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
	 * @return {Function} Returns the anonymous function that was created and assigned as the listener. This is needed to remove the listener later using .removeEventListener.
	 **/
	p.on = function(type, listener, scope, once, data, useCapture) {
		if (listener.handleEvent) {
			scope = scope||listener;
			listener = listener.handleEvent;
		}
		scope = scope||this;
		return this.addEventListener(type, function(evt) {
				listener.call(scope, evt, data);
				once&&evt.remove();
			}, useCapture);
	};

	/**
	 * Removes the specified event listener.
	 *
	 * <b>Important Note:</b> that you must pass the exact function reference used when the event was added. If a proxy
	 * function, or function closure is used as the callback, the proxy/closure reference must be used - a new proxy or
	 * closure will not work.
	 *
	 * <h4>Example</h4>
	 *
	 *      displayObject.removeEventListener("click", handleClick);
	 *
	 * @method removeEventListener
	 * @param {String} type The string type of the event.
	 * @param {Function | Object} listener The listener function or object.
	 * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
	 **/
	p.removeEventListener = function(type, listener, useCapture) {
		var listeners = useCapture ? this._captureListeners : this._listeners;
		if (!listeners) { return; }
		var arr = listeners[type];
		if (!arr) { return; }
		for (var i=0,l=arr.length; i<l; i++) {
			if (arr[i] == listener) {
				if (l==1) { delete(listeners[type]); } // allows for faster checks.
				else { arr.splice(i,1); }
				break;
			}
		}
	};
	
	/**
	 * A shortcut to the removeEventListener method, with the same parameters and return value. This is a companion to the
	 * .on method.
	 * 
	 * <b>IMPORTANT:</b> To remove a listener added with `on`, you must pass in the returned wrapper function as the listener. See 
	 * {{#crossLink "EventDispatcher/on"}}{{/crossLink}} for an example.
	 *
	 * @method off
	 * @param {String} type The string type of the event.
	 * @param {Function | Object} listener The listener function or object.
	 * @param {Boolean} [useCapture] For events that bubble, indicates whether to listen for the event in the capture or bubbling/target phase.
	 **/
	p.off = p.removeEventListener;

	/**
	 * Removes all listeners for the specified type, or all listeners of all types.
	 *
	 * <h4>Example</h4>
	 *
	 *      // Remove all listeners
	 *      displayObject.removeAllEventListeners();
	 *
	 *      // Remove all click listeners
	 *      displayObject.removeAllEventListeners("click");
	 *
	 * @method removeAllEventListeners
	 * @param {String} [type] The string type of the event. If omitted, all listeners for all types will be removed.
	 **/
	p.removeAllEventListeners = function(type) {
		if (!type) { this._listeners = this._captureListeners = null; }
		else {
			if (this._listeners) { delete(this._listeners[type]); }
			if (this._captureListeners) { delete(this._captureListeners[type]); }
		}
	};

	/**
	 * Dispatches the specified event to all listeners.
	 *
	 * <h4>Example</h4>
	 *
	 *      // Use a string event
	 *      this.dispatchEvent("complete");
	 *
	 *      // Use an Event instance
	 *      var event = new createjs.Event("progress");
	 *      this.dispatchEvent(event);
	 *
	 * @method dispatchEvent
	 * @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
	 * While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
	 * dispatchEvent will construct an Event instance if necessary with the specified type. This latter approach can
	 * be used to avoid event object instantiation for non-bubbling events that may not have any listeners.
	 * @param {Boolean} [bubbles] Specifies the `bubbles` value when a string was passed to eventObj.
	 * @param {Boolean} [cancelable] Specifies the `cancelable` value when a string was passed to eventObj.
	 * @return {Boolean} Returns false if `preventDefault()` was called on a cancelable event, true otherwise.
	 **/
	p.dispatchEvent = function(eventObj, bubbles, cancelable) {
		if (typeof eventObj == "string") {
			// skip everything if there's no listeners and it doesn't bubble:
			var listeners = this._listeners;
			if (!bubbles && (!listeners || !listeners[eventObj])) { return true; }
			eventObj = new createjs.Event(eventObj, bubbles, cancelable);
		} else if (eventObj.target && eventObj.clone) {
			// redispatching an active event object, so clone it:
			eventObj = eventObj.clone();
		}
		
		// TODO: it would be nice to eliminate this. Maybe in favour of evtObj instanceof Event? Or !!evtObj.createEvent
		try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events

		if (!eventObj.bubbles || !this.parent) {
			this._dispatchEvent(eventObj, 2);
		} else {
			var top=this, list=[top];
			while (top.parent) { list.push(top = top.parent); }
			var i, l=list.length;

			// capture & atTarget
			for (i=l-1; i>=0 && !eventObj.propagationStopped; i--) {
				list[i]._dispatchEvent(eventObj, 1+(i==0));
			}
			// bubbling
			for (i=1; i<l && !eventObj.propagationStopped; i++) {
				list[i]._dispatchEvent(eventObj, 3);
			}
		}
		return !eventObj.defaultPrevented;
	};

	/**
	 * Indicates whether there is at least one listener for the specified event type.
	 * @method hasEventListener
	 * @param {String} type The string type of the event.
	 * @return {Boolean} Returns true if there is at least one listener for the specified event.
	 **/
	p.hasEventListener = function(type) {
		var listeners = this._listeners, captureListeners = this._captureListeners;
		return !!((listeners && listeners[type]) || (captureListeners && captureListeners[type]));
	};
	
	/**
	 * Indicates whether there is at least one listener for the specified event type on this object or any of its
	 * ancestors (parent, parent's parent, etc). A return value of true indicates that if a bubbling event of the
	 * specified type is dispatched from this object, it will trigger at least one listener.
	 * 
	 * This is similar to {{#crossLink "EventDispatcher/hasEventListener"}}{{/crossLink}}, but it searches the entire
	 * event flow for a listener, not just this object.
	 * @method willTrigger
	 * @param {String} type The string type of the event.
	 * @return {Boolean} Returns `true` if there is at least one listener for the specified event.
	 **/
	p.willTrigger = function(type) {
		var o = this;
		while (o) {
			if (o.hasEventListener(type)) { return true; }
			o = o.parent;
		}
		return false;
	};

	/**
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[EventDispatcher]";
	};


// private methods:
	/**
	 * @method _dispatchEvent
	 * @param {Object | String | Event} eventObj
	 * @param {Object} eventPhase
	 * @protected
	 **/
	p._dispatchEvent = function(eventObj, eventPhase) {
		var l, listeners = (eventPhase==1) ? this._captureListeners : this._listeners;
		if (eventObj && listeners) {
			var arr = listeners[eventObj.type];
			if (!arr||!(l=arr.length)) { return; }
			try { eventObj.currentTarget = this; } catch (e) {}
			try { eventObj.eventPhase = eventPhase; } catch (e) {}
			eventObj.removed = false;
			
			arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
			for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) {
				var o = arr[i];
				if (o.handleEvent) { o.handleEvent(eventObj); }
				else { o(eventObj); }
				if (eventObj.removed) {
					this.off(eventObj.type, o, eventPhase==1);
					eventObj.removed = false;
				}
			}
		}
	};


	createjs.EventDispatcher = EventDispatcher;
}());

//##############################################################################
// Ticker.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * The Ticker provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe to the tick
	 * event to be notified when a set time interval has elapsed.
	 *
	 * Note that the interval that the tick event is called is a target interval, and may be broadcast at a slower interval
	 * when under high CPU load. The Ticker class uses a static interface (ex. `Ticker.framerate = 30;`) and
	 * can not be instantiated.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.Ticker.addEventListener("tick", handleTick);
	 *      function handleTick(event) {
	 *          // Actions carried out each tick (aka frame)
	 *          if (!event.paused) {
	 *              // Actions carried out when the Ticker is not paused.
	 *          }
	 *      }
	 *
	 * @class Ticker
	 * @uses EventDispatcher
	 * @static
	 **/
	function Ticker() {
		throw "Ticker cannot be instantiated.";
	}


// constants:
	/**
	 * In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It
	 * uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and
	 * dispatches the tick when the time is within a certain threshold.
	 *
	 * This mode has a higher variance for time between frames than {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}},
	 * but does not require that content be time based as with {{#crossLink "Ticker/RAF:property"}}{{/crossLink}} while
	 * gaining the benefits of that API (screen synch, background throttling).
	 *
	 * Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so
	 * framerates of 10, 12, 15, 20, and 30 work well.
	 *
	 * Falls back to {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
	 * supported.
	 * @property RAF_SYNCHED
	 * @static
	 * @type {String}
	 * @default "synched"
	 * @readonly
	 **/
	Ticker.RAF_SYNCHED = "synched";

	/**
	 * In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely.
	 * Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based.
	 * You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
	 * event object's "delta" properties to make this easier.
	 *
	 * Falls back on {{#crossLink "Ticker/TIMEOUT:property"}}{{/crossLink}} if the requestAnimationFrame API is not
	 * supported.
	 * @property RAF
	 * @static
	 * @type {String}
	 * @default "raf"
	 * @readonly
	 **/
	Ticker.RAF = "raf";

	/**
	 * In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not
	 * provide the benefits of requestAnimationFrame (screen synch, background throttling).
	 * @property TIMEOUT
	 * @static
	 * @type {String}
	 * @default "timeout"
	 * @readonly
	 **/
	Ticker.TIMEOUT = "timeout";


// static events:
	/**
	 * Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using
	 * {{#crossLink "Ticker/setPaused"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.Ticker.addEventListener("tick", handleTick);
	 *      function handleTick(event) {
	 *          console.log("Paused:", event.paused, event.delta);
	 *      }
	 *
	 * @event tick
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {Boolean} paused Indicates whether the ticker is currently paused.
	 * @param {Number} delta The time elapsed in ms since the last tick.
	 * @param {Number} time The total time in ms since Ticker was initialized.
	 * @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example,
	 * 	you could determine the amount of time that the Ticker has been paused since initialization with `time-runTime`.
	 * @since 0.6.0
	 */


// public static properties:
	/**
	 * Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}, and will be removed in a future version. If true, timingMode will
	 * use {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} by default.
	 * @deprecated Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}.
	 * @property useRAF
	 * @static
	 * @type {Boolean}
	 * @default false
	 **/
	Ticker.useRAF = false;

	/**
	 * Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See
	 * {{#crossLink "Ticker/TIMEOUT"}}{{/crossLink}}, {{#crossLink "Ticker/RAF"}}{{/crossLink}}, and
	 * {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} for mode details.
	 * @property timingMode
	 * @static
	 * @type {String}
	 * @default Ticker.TIMEOUT
	 **/
	Ticker.timingMode = null;

	/**
	 * Specifies a maximum value for the delta property in the tick event object. This is useful when building time
	 * based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep,
	 * alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value
	 * (ex. maxDelta=50 when running at 40fps).
	 * 
	 * This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta
	 * when using both delta and other values.
	 * 
	 * If 0, there is no maximum.
	 * @property maxDelta
	 * @static
	 * @type {number}
	 * @default 0
	 */
	Ticker.maxDelta = 0;
	
	/**
	 * When the ticker is paused, all listeners will still receive a tick event, but the <code>paused</code> property
	 * of the event will be `true`. Also, while paused the `runTime` will not increase. See {{#crossLink "Ticker/tick:event"}}{{/crossLink}},
	 * {{#crossLink "Ticker/getTime"}}{{/crossLink}}, and {{#crossLink "Ticker/getEventTime"}}{{/crossLink}} for more
	 * info.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.Ticker.addEventListener("tick", handleTick);
	 *      createjs.Ticker.paused = true;
	 *      function handleTick(event) {
	 *          console.log(event.paused,
	 *          	createjs.Ticker.getTime(false),
	 *          	createjs.Ticker.getTime(true));
	 *      }
	 *
	 * @property paused
	 * @static
	 * @type {Boolean}
	 * @default false
	 **/
	Ticker.paused = false;


// mix-ins:
	// EventDispatcher methods:
	Ticker.removeEventListener = null;
	Ticker.removeAllEventListeners = null;
	Ticker.dispatchEvent = null;
	Ticker.hasEventListener = null;
	Ticker._listeners = null;
	createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods.
	Ticker._addEventListener = Ticker.addEventListener;
	Ticker.addEventListener = function() {
		!Ticker._inited&&Ticker.init();
		return Ticker._addEventListener.apply(Ticker, arguments);
	};


// private static properties:
	/**
	 * @property _inited
	 * @static
	 * @type {Boolean}
	 * @protected
	 **/
	Ticker._inited = false;

	/**
	 * @property _startTime
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._startTime = 0;

	/**
	 * @property _pausedTime
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._pausedTime=0;

	/**
	 * The number of ticks that have passed
	 * @property _ticks
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._ticks = 0;

	/**
	 * The number of ticks that have passed while Ticker has been paused
	 * @property _pausedTicks
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._pausedTicks = 0;

	/**
	 * @property _interval
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._interval = 50;

	/**
	 * @property _lastTime
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._lastTime = 0;

	/**
	 * @property _times
	 * @static
	 * @type {Array}
	 * @protected
	 **/
	Ticker._times = null;

	/**
	 * @property _tickTimes
	 * @static
	 * @type {Array}
	 * @protected
	 **/
	Ticker._tickTimes = null;

	/**
	 * Stores the timeout or requestAnimationFrame id.
	 * @property _timerId
	 * @static
	 * @type {Number}
	 * @protected
	 **/
	Ticker._timerId = null;
	
	/**
	 * True if currently using requestAnimationFrame, false if using setTimeout. This may be different than timingMode
	 * if that property changed and a tick hasn't fired.
	 * @property _raf
	 * @static
	 * @type {Boolean}
	 * @protected
	 **/
	Ticker._raf = true;
	

// static getter / setters:
	/**
	 * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
	 * @method setInterval
	 * @static
	 * @param {Number} interval
	 * @deprecated
	 **/
	Ticker.setInterval = function(interval) {
		Ticker._interval = interval;
		if (!Ticker._inited) { return; }
		Ticker._setupTick();
	};

	/**
	 * Use the {{#crossLink "Ticker/interval:property"}}{{/crossLink}} property instead.
	 * @method getInterval
	 * @static
	 * @return {Number}
	 * @deprecated
	 **/
	Ticker.getInterval = function() {
		return Ticker._interval;
	};

	/**
	 * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
	 * @method setFPS
	 * @static
	 * @param {Number} value
	 * @deprecated
	 **/
	Ticker.setFPS = function(value) {
		Ticker.setInterval(1000/value);
	};

	/**
	 * Use the {{#crossLink "Ticker/framerate:property"}}{{/crossLink}} property instead.
	 * @method getFPS
	 * @static
	 * @return {Number}
	 * @deprecated
	 **/
	Ticker.getFPS = function() {
		return 1000/Ticker._interval;
	};

	/**
	 * Indicates the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
	 * Note that actual time between ticks may be more than specified depending on CPU load.
	 * This property is ignored if the ticker is using the `RAF` timing mode.
	 * @property interval
	 * @static
	 * @type {Number}
	 **/
	 
	/**
	 * Indicates the target frame rate in frames per second (FPS). Effectively just a shortcut to `interval`, where
	 * `framerate == 1000/interval`.
	 * @property framerate
	 * @static
	 * @type {Number}
	 **/
	try {
		Object.defineProperties(Ticker, {
			interval: { get: Ticker.getInterval, set: Ticker.setInterval },
			framerate: { get: Ticker.getFPS, set: Ticker.setFPS }
		});
	} catch (e) { console.log(e); }


// public static methods:
	/**
	 * Starts the tick. This is called automatically when the first listener is added.
	 * @method init
	 * @static
	 **/
	Ticker.init = function() {
		if (Ticker._inited) { return; }
		Ticker._inited = true;
		Ticker._times = [];
		Ticker._tickTimes = [];
		Ticker._startTime = Ticker._getTime();
		Ticker._times.push(Ticker._lastTime = 0);
		Ticker.interval = Ticker._interval;
	};
	
	/**
	 * Stops the Ticker and removes all listeners. Use init() to restart the Ticker.
	 * @method reset
	 * @static
	 **/
	Ticker.reset = function() {
		if (Ticker._raf) {
			var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame;
			f&&f(Ticker._timerId);
		} else {
			clearTimeout(Ticker._timerId);
		}
		Ticker.removeAllEventListeners("tick");
		Ticker._timerId = Ticker._times = Ticker._tickTimes = null;
		Ticker._startTime = Ticker._lastTime = Ticker._ticks = 0;
		Ticker._inited = false;
	};

	/**
	 * Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS
	 * because it only measures the time spent within the tick execution stack. 
	 * 
	 * Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between 
	 * the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that 
	 * there may be up to 35ms of "idle" time between the end of one tick and the start of the next.
	 *
	 * Example 2: With a target FPS of 30, getFPS() returns 10fps, which indicates an average of 100ms between the end of
	 * one tick and the end of the next. However, getMeasuredTickTime() returns 20ms. This would indicate that something
	 * other than the tick is using ~80ms (another script, DOM rendering, etc).
	 * @method getMeasuredTickTime
	 * @static
	 * @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick.
	 * Defaults to the number of ticks per second. To get only the last tick's time, pass in 1.
	 * @return {Number} The average time spent in a tick in milliseconds.
	 **/
	Ticker.getMeasuredTickTime = function(ticks) {
		var ttl=0, times=Ticker._tickTimes;
		if (!times || times.length < 1) { return -1; }

		// by default, calculate average for the past ~1 second:
		ticks = Math.min(times.length, ticks||(Ticker.getFPS()|0));
		for (var i=0; i<ticks; i++) { ttl += times[i]; }
		return ttl/ticks;
	};

	/**
	 * Returns the actual frames / ticks per second.
	 * @method getMeasuredFPS
	 * @static
	 * @param {Number} [ticks] The number of previous ticks over which to measure the actual frames / ticks per second.
	 * Defaults to the number of ticks per second.
	 * @return {Number} The actual frames / ticks per second. Depending on performance, this may differ
	 * from the target frames per second.
	 **/
	Ticker.getMeasuredFPS = function(ticks) {
		var times = Ticker._times;
		if (!times || times.length < 2) { return -1; }

		// by default, calculate fps for the past ~1 second:
		ticks = Math.min(times.length-1, ticks||(Ticker.getFPS()|0));
		return 1000/((times[0]-times[ticks])/ticks);
	};

	/**
	 * Use the {{#crossLink "Ticker/paused:property"}}{{/crossLink}} property instead.
	 * @method setPaused
	 * @static
	 * @param {Boolean} value
	 * @deprecated
	 **/
	Ticker.setPaused = function(value) {
		// TODO: deprecated.
		Ticker.paused = value;
	};

	/**
	 * Use the {{#crossLink "Ticker/paused:property"}}{{/crossLink}} property instead.
	 * @method getPaused
	 * @static
	 * @return {Boolean}
	 * @deprecated
	 **/
	Ticker.getPaused = function() {
		// TODO: deprecated.
		return Ticker.paused;
	};

	/**
	 * Returns the number of milliseconds that have elapsed since Ticker was initialized via {{#crossLink "Ticker/init"}}.
	 * Returns -1 if Ticker has not been initialized. For example, you could use
	 * this in a time synchronized animation to determine the exact amount of time that has elapsed.
	 * @method getTime
	 * @static
	 * @param {Boolean} [runTime=false] If true only time elapsed while Ticker was not paused will be returned.
	 * If false, the value returned will be total time elapsed since the first tick event listener was added.
	 * @return {Number} Number of milliseconds that have elapsed since Ticker was initialized or -1.
	 **/
	Ticker.getTime = function(runTime) {
		return Ticker._startTime ? Ticker._getTime() - (runTime ? Ticker._pausedTime : 0) : -1;
	};

	/**
	 * Similar to the {{#crossLink "Ticker/getTime"}}{{/crossLink}} method, but returns the time on the most recent {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
	 * event object.
	 * @method getEventTime
	 * @static
	 * @param runTime {Boolean} [runTime=false] If true, the runTime property will be returned instead of time.
	 * @returns {number} The time or runTime property from the most recent tick event or -1.
	 */
	Ticker.getEventTime = function(runTime) {
		return Ticker._startTime ? (Ticker._lastTime || Ticker._startTime) - (runTime ? Ticker._pausedTime : 0) : -1;
	};
	
	/**
	 * Returns the number of ticks that have been broadcast by Ticker.
	 * @method getTicks
	 * @static
	 * @param {Boolean} pauseable Indicates whether to include ticks that would have been broadcast
	 * while Ticker was paused. If true only tick events broadcast while Ticker is not paused will be returned.
	 * If false, tick events that would have been broadcast while Ticker was paused will be included in the return
	 * value. The default value is false.
	 * @return {Number} of ticks that have been broadcast.
	 **/
	Ticker.getTicks = function(pauseable) {
		return  Ticker._ticks - (pauseable ? Ticker._pausedTicks : 0);
	};


// private static methods:
	/**
	 * @method _handleSynch
	 * @static
	 * @protected
	 **/
	Ticker._handleSynch = function() {
		Ticker._timerId = null;
		Ticker._setupTick();

		// run if enough time has elapsed, with a little bit of flexibility to be early:
		if (Ticker._getTime() - Ticker._lastTime >= (Ticker._interval-1)*0.97) {
			Ticker._tick();
		}
	};

	/**
	 * @method _handleRAF
	 * @static
	 * @protected
	 **/
	Ticker._handleRAF = function() {
		Ticker._timerId = null;
		Ticker._setupTick();
		Ticker._tick();
	};

	/**
	 * @method _handleTimeout
	 * @static
	 * @protected
	 **/
	Ticker._handleTimeout = function() {
		Ticker._timerId = null;
		Ticker._setupTick();
		Ticker._tick();
	};

	/**
	 * @method _setupTick
	 * @static
	 * @protected
	 **/
	Ticker._setupTick = function() {
		if (Ticker._timerId != null) { return; } // avoid duplicates

		var mode = Ticker.timingMode||(Ticker.useRAF&&Ticker.RAF_SYNCHED);
		if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) {
			var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
			if (f) {
				Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch);
				Ticker._raf = true;
				return;
			}
		}
		Ticker._raf = false;
		Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval);
	};

	/**
	 * @method _tick
	 * @static
	 * @protected
	 **/
	Ticker._tick = function() {
		var paused = Ticker.paused;
		var time = Ticker._getTime();
		var elapsedTime = time-Ticker._lastTime;
		Ticker._lastTime = time;
		Ticker._ticks++;
		
		if (paused) {
			Ticker._pausedTicks++;
			Ticker._pausedTime += elapsedTime;
		}
		
		if (Ticker.hasEventListener("tick")) {
			var event = new createjs.Event("tick");
			var maxDelta = Ticker.maxDelta;
			event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime;
			event.paused = paused;
			event.time = time;
			event.runTime = time-Ticker._pausedTime;
			Ticker.dispatchEvent(event);
		}
		
		Ticker._tickTimes.unshift(Ticker._getTime()-time);
		while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); }

		Ticker._times.unshift(time);
		while (Ticker._times.length > 100) { Ticker._times.pop(); }
	};

	/**
	 * @method _getTime
	 * @static
	 * @protected
	 **/
	var now = window.performance && (performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow);
	Ticker._getTime = function() {
		return ((now&&now.call(performance))||(new Date().getTime())) - Ticker._startTime;
	};


	createjs.Ticker = Ticker;
}());

//##############################################################################
// UID.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Global utility for generating sequential unique ID numbers. The UID class uses a static interface (ex. <code>UID.get()</code>)
	 * and should not be instantiated.
	 * @class UID
	 * @static
	 **/
	function UID() {
		throw "UID cannot be instantiated";
	}


// private static properties:
	/**
	 * @property _nextID
	 * @type Number
	 * @protected
	 **/
	UID._nextID = 0;


// public static methods:
	/**
	 * Returns the next unique id.
	 * @method get
	 * @return {Number} The next unique id
	 * @static
	 **/
	UID.get = function() {
		return UID._nextID++;
	};


	createjs.UID = UID;
}());

//##############################################################################
// MouseEvent.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Passed as the parameter to all mouse/pointer/touch related events. For a listing of mouse events and their properties,
	 * see the {{#crossLink "DisplayObject"}}{{/crossLink}} and {{#crossLink "Stage"}}{{/crossLink}} event listings.
	 * @class MouseEvent
	 * @param {String} type The event type.
	 * @param {Boolean} bubbles Indicates whether the event will bubble through the display list.
	 * @param {Boolean} cancelable Indicates whether the default behaviour of this event can be cancelled.
	 * @param {Number} stageX The normalized x position relative to the stage.
	 * @param {Number} stageY The normalized y position relative to the stage.
	 * @param {MouseEvent} nativeEvent The native DOM event related to this mouse event.
	 * @param {Number} pointerID The unique id for the pointer.
	 * @param {Boolean} primary Indicates whether this is the primary pointer in a multitouch environment.
	 * @param {Number} rawX The raw x position relative to the stage.
	 * @param {Number} rawY The raw y position relative to the stage.
	 * @param {DisplayObject} relatedTarget The secondary target for the event.
	 * @extends Event
	 * @constructor
	 **/
	function MouseEvent(type, bubbles, cancelable, stageX, stageY, nativeEvent, pointerID, primary, rawX, rawY, relatedTarget) {
		this.Event_constructor(type, bubbles, cancelable);
		
		
	// public properties:
		/**
		 * The normalized x position on the stage. This will always be within the range 0 to stage width.
		 * @property stageX
		 * @type Number
		*/
		this.stageX = stageX;
	
		/**
		 * The normalized y position on the stage. This will always be within the range 0 to stage height.
		 * @property stageY
		 * @type Number
		 **/
		this.stageY = stageY;
	
		/**
		 * The raw x position relative to the stage. Normally this will be the same as the stageX value, unless
		 * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
		 * @property rawX
		 * @type Number
		*/
		this.rawX = (rawX==null)?stageX:rawX;
	
		/**
		 * The raw y position relative to the stage. Normally this will be the same as the stageY value, unless
		 * stage.mouseMoveOutside is true and the pointer is outside of the stage bounds.
		 * @property rawY
		 * @type Number
		*/
		this.rawY = (rawY==null)?stageY:rawY;
	
		/**
		 * The native MouseEvent generated by the browser. The properties and API for this
		 * event may differ between browsers. This property will be null if the
		 * EaselJS property was not directly generated from a native MouseEvent.
		 * @property nativeEvent
		 * @type HtmlMouseEvent
		 * @default null
		 **/
		this.nativeEvent = nativeEvent;
	
		/**
		 * The unique id for the pointer (touch point or cursor). This will be either -1 for the mouse, or the system
		 * supplied id value.
		 * @property pointerID
		 * @type {Number}
		 */
		this.pointerID = pointerID;
	
		/**
		 * Indicates whether this is the primary pointer in a multitouch environment. This will always be true for the mouse.
		 * For touch pointers, the first pointer in the current stack will be considered the primary pointer.
		 * @property primary
		 * @type {Boolean}
		 */
		this.primary = !!primary;
		
		/**
		 * The secondary target for the event, if applicable. This is used for mouseout/rollout
		 * events to indicate the object that the mouse entered from, mouseover/rollover for the object the mouse exited,
		 * and stagemousedown/stagemouseup events for the object that was the under the cursor, if any.
		 * 
		 * Only valid interaction targets will be returned (ie. objects with mouse listeners or a cursor set).
		 * @property relatedTarget
		 * @type {DisplayObject}
		 */
		this.relatedTarget = relatedTarget;
	}
	var p = createjs.extend(MouseEvent, createjs.Event);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
	
	
// getter / setters:
	/**
	 * Returns the x position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
	 * @property localX
	 * @type {Number}
	 * @readonly
	 */
	p._get_localX = function() {
		return this.currentTarget.globalToLocal(this.rawX, this.rawY).x;
	};
	
	/**
	 * Returns the y position of the mouse in the local coordinate system of the current target (ie. the dispatcher).
	 * @property localY
	 * @type {Number}
	 * @readonly
	 */
	p._get_localY = function() {
		return this.currentTarget.globalToLocal(this.rawX, this.rawY).y;
	};
	
	/**
	 * Indicates whether the event was generated by a touch input (versus a mouse input).
	 * @property isTouch
	 * @type {Boolean}
	 * @readonly
	 */
	p._get_isTouch = function() {
		return this.pointerID !== -1;
	};
	
	
	try {
		Object.defineProperties(p, {
			localX: { get: p._get_localX },
			localY: { get: p._get_localY },
			isTouch: { get: p._get_isTouch }
		});
	} catch (e) {} // TODO: use Log


// public methods:
	/**
	 * Returns a clone of the MouseEvent instance.
	 * @method clone
	 * @return {MouseEvent} a clone of the MouseEvent instance.
	 **/
	p.clone = function() {
		return new MouseEvent(this.type, this.bubbles, this.cancelable, this.stageX, this.stageY, this.nativeEvent, this.pointerID, this.primary, this.rawX, this.rawY);
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[MouseEvent (type="+this.type+" stageX="+this.stageX+" stageY="+this.stageY+")]";
	};


	createjs.MouseEvent = createjs.promote(MouseEvent, "Event");
}());

//##############################################################################
// Matrix2D.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Represents an affine transformation matrix, and provides tools for constructing and concatenating matrices.
	 *
	 * This matrix can be visualized as:
	 *
	 * 	[ a  c  tx
	 * 	  b  d  ty
	 * 	  0  0  1  ]
	 *
	 * Note the locations of b and c.
	 *
	 * @class Matrix2D
	 * @param {Number} [a=1] Specifies the a property for the new matrix.
	 * @param {Number} [b=0] Specifies the b property for the new matrix.
	 * @param {Number} [c=0] Specifies the c property for the new matrix.
	 * @param {Number} [d=1] Specifies the d property for the new matrix.
	 * @param {Number} [tx=0] Specifies the tx property for the new matrix.
	 * @param {Number} [ty=0] Specifies the ty property for the new matrix.
	 * @constructor
	 **/
	function Matrix2D(a, b, c, d, tx, ty) {
		this.setValues(a,b,c,d,tx,ty);
		
	// public properties:
		// assigned in the setValues method.
		/**
		 * Position (0, 0) in a 3x3 affine transformation matrix.
		 * @property a
		 * @type Number
		 **/
	
		/**
		 * Position (0, 1) in a 3x3 affine transformation matrix.
		 * @property b
		 * @type Number
		 **/
	
		/**
		 * Position (1, 0) in a 3x3 affine transformation matrix.
		 * @property c
		 * @type Number
		 **/
	
		/**
		 * Position (1, 1) in a 3x3 affine transformation matrix.
		 * @property d
		 * @type Number
		 **/
	
		/**
		 * Position (2, 0) in a 3x3 affine transformation matrix.
		 * @property tx
		 * @type Number
		 **/
	
		/**
		 * Position (2, 1) in a 3x3 affine transformation matrix.
		 * @property ty
		 * @type Number
		 **/
	}
	var p = Matrix2D.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// constants:
	/**
	 * Multiplier for converting degrees to radians. Used internally by Matrix2D.
	 * @property DEG_TO_RAD
	 * @static
	 * @final
	 * @type Number
	 * @readonly
	 **/
	Matrix2D.DEG_TO_RAD = Math.PI/180;


// static public properties:
	/**
	 * An identity matrix, representing a null transformation.
	 * @property identity
	 * @static
	 * @type Matrix2D
	 * @readonly
	 **/
	Matrix2D.identity = null; // set at bottom of class definition.
	

// public methods:
	/**
	 * Sets the specified values on this instance. 
	 * @method setValues
	 * @param {Number} [a=1] Specifies the a property for the new matrix.
	 * @param {Number} [b=0] Specifies the b property for the new matrix.
	 * @param {Number} [c=0] Specifies the c property for the new matrix.
	 * @param {Number} [d=1] Specifies the d property for the new matrix.
	 * @param {Number} [tx=0] Specifies the tx property for the new matrix.
	 * @param {Number} [ty=0] Specifies the ty property for the new matrix.
	 * @return {Matrix2D} This instance. Useful for chaining method calls.
	*/
	p.setValues = function(a, b, c, d, tx, ty) {
		// don't forget to update docs in the constructor if these change:
		this.a = (a == null) ? 1 : a;
		this.b = b || 0;
		this.c = c || 0;
		this.d = (d == null) ? 1 : d;
		this.tx = tx || 0;
		this.ty = ty || 0;
		return this;
	};

	/**
	 * Appends the specified matrix properties to this matrix. All parameters are required.
	 * This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
	 * @method append
	 * @param {Number} a
	 * @param {Number} b
	 * @param {Number} c
	 * @param {Number} d
	 * @param {Number} tx
	 * @param {Number} ty
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.append = function(a, b, c, d, tx, ty) {
		var a1 = this.a;
		var b1 = this.b;
		var c1 = this.c;
		var d1 = this.d;
		if (a != 1 || b != 0 || c != 0 || d != 1) {
			this.a  = a1*a+c1*b;
			this.b  = b1*a+d1*b;
			this.c  = a1*c+c1*d;
			this.d  = b1*c+d1*d;
		}
		this.tx = a1*tx+c1*ty+this.tx;
		this.ty = b1*tx+d1*ty+this.ty;
		return this;
	};

	/**
	 * Prepends the specified matrix properties to this matrix.
	 * This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
	 * All parameters are required.
	 * @method prepend
	 * @param {Number} a
	 * @param {Number} b
	 * @param {Number} c
	 * @param {Number} d
	 * @param {Number} tx
	 * @param {Number} ty
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.prepend = function(a, b, c, d, tx, ty) {
		var a1 = this.a;
		var c1 = this.c;
		var tx1 = this.tx;

		this.a  = a*a1+c*this.b;
		this.b  = b*a1+d*this.b;
		this.c  = a*c1+c*this.d;
		this.d  = b*c1+d*this.d;
		this.tx = a*tx1+c*this.ty+tx;
		this.ty = b*tx1+d*this.ty+ty;
		return this;
	};

	/**
	 * Appends the specified matrix to this matrix.
	 * This is the equivalent of multiplying `(this matrix) * (specified matrix)`.
	 * @method appendMatrix
	 * @param {Matrix2D} matrix
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.appendMatrix = function(matrix) {
		return this.append(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
	};

	/**
	 * Prepends the specified matrix to this matrix.
	 * This is the equivalent of multiplying `(specified matrix) * (this matrix)`.
	 * For example, you could calculate the combined transformation for a child object using:
	 * 
	 * 	var o = myDisplayObject;
	 * 	var mtx = o.getMatrix();
	 * 	while (o = o.parent) {
	 * 		// prepend each parent's transformation in turn:
	 * 		o.prependMatrix(o.getMatrix());
	 * 	}
	 * @method prependMatrix
	 * @param {Matrix2D} matrix
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.prependMatrix = function(matrix) {
		return this.prepend(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
	};

	/**
	 * Generates matrix properties from the specified display object transform properties, and appends them to this matrix.
	 * For example, you can use this to generate a matrix representing the transformations of a display object:
	 * 
	 * 	var mtx = new createjs.Matrix2D();
	 * 	mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation);
	 * @method appendTransform
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} scaleX
	 * @param {Number} scaleY
	 * @param {Number} rotation
	 * @param {Number} skewX
	 * @param {Number} skewY
	 * @param {Number} regX Optional.
	 * @param {Number} regY Optional.
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.appendTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
		if (rotation%360) {
			var r = rotation*Matrix2D.DEG_TO_RAD;
			var cos = Math.cos(r);
			var sin = Math.sin(r);
		} else {
			cos = 1;
			sin = 0;
		}

		if (skewX || skewY) {
			// TODO: can this be combined into a single append operation?
			skewX *= Matrix2D.DEG_TO_RAD;
			skewY *= Matrix2D.DEG_TO_RAD;
			this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
			this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
		} else {
			this.append(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
		}
		
		if (regX || regY) {
			// append the registration offset:
			this.tx -= regX*this.a+regY*this.c; 
			this.ty -= regX*this.b+regY*this.d;
		}
		return this;
	};

	/**
	 * Generates matrix properties from the specified display object transform properties, and prepends them to this matrix.
	 * For example, you could calculate the combined transformation for a child object using:
	 * 
	 * 	var o = myDisplayObject;
	 * 	var mtx = new createjs.Matrix2D();
	 * 	do  {
	 * 		// prepend each parent's transformation in turn:
	 * 		mtx.prependTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
	 * 	} while (o = o.parent);
	 * 	
	 * 	Note that the above example would not account for {{#crossLink "DisplayObject/transformMatrix:property"}}{{/crossLink}}
	 * 	values. See {{#crossLink "Matrix2D/prependMatrix"}}{{/crossLink}} for an example that does.
	 * @method prependTransform
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} scaleX
	 * @param {Number} scaleY
	 * @param {Number} rotation
	 * @param {Number} skewX
	 * @param {Number} skewY
	 * @param {Number} regX Optional.
	 * @param {Number} regY Optional.
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.prependTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
		if (rotation%360) {
			var r = rotation*Matrix2D.DEG_TO_RAD;
			var cos = Math.cos(r);
			var sin = Math.sin(r);
		} else {
			cos = 1;
			sin = 0;
		}

		if (regX || regY) {
			// prepend the registration offset:
			this.tx -= regX; this.ty -= regY;
		}
		if (skewX || skewY) {
			// TODO: can this be combined into a single prepend operation?
			skewX *= Matrix2D.DEG_TO_RAD;
			skewY *= Matrix2D.DEG_TO_RAD;
			this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, 0, 0);
			this.prepend(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), x, y);
		} else {
			this.prepend(cos*scaleX, sin*scaleX, -sin*scaleY, cos*scaleY, x, y);
		}
		return this;
	};

	/**
	 * Applies a clockwise rotation transformation to the matrix.
	 * @method rotate
	 * @param {Number} angle The angle to rotate by, in degrees. To use a value in radians, multiply it by `180/Math.PI`.
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.rotate = function(angle) {
		angle = angle*Matrix2D.DEG_TO_RAD;
		var cos = Math.cos(angle);
		var sin = Math.sin(angle);

		var a1 = this.a;
		var b1 = this.b;

		this.a = a1*cos+this.c*sin;
		this.b = b1*cos+this.d*sin;
		this.c = -a1*sin+this.c*cos;
		this.d = -b1*sin+this.d*cos;
		return this;
	};

	/**
	 * Applies a skew transformation to the matrix.
	 * @method skew
	 * @param {Number} skewX The amount to skew horizontally in degrees. To use a value in radians, multiply it by `180/Math.PI`.
	 * @param {Number} skewY The amount to skew vertically in degrees.
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	*/
	p.skew = function(skewX, skewY) {
		skewX = skewX*Matrix2D.DEG_TO_RAD;
		skewY = skewY*Matrix2D.DEG_TO_RAD;
		this.append(Math.cos(skewY), Math.sin(skewY), -Math.sin(skewX), Math.cos(skewX), 0, 0);
		return this;
	};

	/**
	 * Applies a scale transformation to the matrix.
	 * @method scale
	 * @param {Number} x The amount to scale horizontally. E.G. a value of 2 will double the size in the X direction, and 0.5 will halve it.
	 * @param {Number} y The amount to scale vertically.
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.scale = function(x, y) {
		this.a *= x;
		this.b *= x;
		this.c *= y;
		this.d *= y;
		//this.tx *= x;
		//this.ty *= y;
		return this;
	};

	/**
	 * Translates the matrix on the x and y axes.
	 * @method translate
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.translate = function(x, y) {
		this.tx += this.a*x + this.c*y;
		this.ty += this.b*x + this.d*y;
		return this;
	};

	/**
	 * Sets the properties of the matrix to those of an identity matrix (one that applies a null transformation).
	 * @method identity
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.identity = function() {
		this.a = this.d = 1;
		this.b = this.c = this.tx = this.ty = 0;
		return this;
	};

	/**
	 * Inverts the matrix, causing it to perform the opposite transformation.
	 * @method invert
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	 **/
	p.invert = function() {
		var a1 = this.a;
		var b1 = this.b;
		var c1 = this.c;
		var d1 = this.d;
		var tx1 = this.tx;
		var n = a1*d1-b1*c1;

		this.a = d1/n;
		this.b = -b1/n;
		this.c = -c1/n;
		this.d = a1/n;
		this.tx = (c1*this.ty-d1*tx1)/n;
		this.ty = -(a1*this.ty-b1*tx1)/n;
		return this;
	};

	/**
	 * Returns true if the matrix is an identity matrix.
	 * @method isIdentity
	 * @return {Boolean}
	 **/
	p.isIdentity = function() {
		return this.tx === 0 && this.ty === 0 && this.a === 1 && this.b === 0 && this.c === 0 && this.d === 1;
	};
	
	/**
	 * Returns true if this matrix is equal to the specified matrix (all property values are equal).
	 * @method equals
	 * @param {Matrix2D} matrix The matrix to compare.
	 * @return {Boolean}
	 **/
	p.equals = function(matrix) {
		return this.tx === matrix.tx && this.ty === matrix.ty && this.a === matrix.a && this.b === matrix.b && this.c === matrix.c && this.d === matrix.d;
	};

	/**
	 * Transforms a point according to this matrix.
	 * @method transformPoint
	 * @param {Number} x The x component of the point to transform.
	 * @param {Number} y The y component of the point to transform.
	 * @param {Point | Object} [pt] An object to copy the result into. If omitted a generic object with x/y properties will be returned.
	 * @return {Point} This matrix. Useful for chaining method calls.
	 **/
	p.transformPoint = function(x, y, pt) {
		pt = pt||{};
		pt.x = x*this.a+y*this.c+this.tx;
		pt.y = x*this.b+y*this.d+this.ty;
		return pt;
	};

	/**
	 * Decomposes the matrix into transform properties (x, y, scaleX, scaleY, and rotation). Note that these values
	 * may not match the transform properties you used to generate the matrix, though they will produce the same visual
	 * results.
	 * @method decompose
	 * @param {Object} target The object to apply the transform properties to. If null, then a new object will be returned.
	 * @return {Object} The target, or a new generic object with the transform properties applied.
	*/
	p.decompose = function(target) {
		// TODO: it would be nice to be able to solve for whether the matrix can be decomposed into only scale/rotation even when scale is negative
		if (target == null) { target = {}; }
		target.x = this.tx;
		target.y = this.ty;
		target.scaleX = Math.sqrt(this.a * this.a + this.b * this.b);
		target.scaleY = Math.sqrt(this.c * this.c + this.d * this.d);

		var skewX = Math.atan2(-this.c, this.d);
		var skewY = Math.atan2(this.b, this.a);

		var delta = Math.abs(1-skewX/skewY);
		if (delta < 0.00001) { // effectively identical, can use rotation:
			target.rotation = skewY/Matrix2D.DEG_TO_RAD;
			if (this.a < 0 && this.d >= 0) {
				target.rotation += (target.rotation <= 0) ? 180 : -180;
			}
			target.skewX = target.skewY = 0;
		} else {
			target.skewX = skewX/Matrix2D.DEG_TO_RAD;
			target.skewY = skewY/Matrix2D.DEG_TO_RAD;
		}
		return target;
	};
	
	/**
	 * Copies all properties from the specified matrix to this matrix.
	 * @method copy
	 * @param {Matrix2D} matrix The matrix to copy properties from.
	 * @return {Matrix2D} This matrix. Useful for chaining method calls.
	*/
	p.copy = function(matrix) {
		return this.setValues(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
	};

	/**
	 * Returns a clone of the Matrix2D instance.
	 * @method clone
	 * @return {Matrix2D} a clone of the Matrix2D instance.
	 **/
	p.clone = function() {
		return new Matrix2D(this.a, this.b, this.c, this.d, this.tx, this.ty);
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Matrix2D (a="+this.a+" b="+this.b+" c="+this.c+" d="+this.d+" tx="+this.tx+" ty="+this.ty+")]";
	};

	// this has to be populated after the class is defined:
	Matrix2D.identity = new Matrix2D();


	createjs.Matrix2D = Matrix2D;
}());

//##############################################################################
// DisplayProps.js
//##############################################################################

(function() {
	"use strict";

	/**
	 * Used for calculating and encapsulating display related properties.
	 * @class DisplayProps
	 * @param {Number} [visible=true] Visible value.
	 * @param {Number} [alpha=1] Alpha value.
	 * @param {Number} [shadow=null] A Shadow instance or null.
	 * @param {Number} [compositeOperation=null] A compositeOperation value or null.
	 * @param {Number} [matrix] A transformation matrix. Defaults to a new identity matrix.
	 * @constructor
	 **/
	function DisplayProps(visible, alpha, shadow, compositeOperation, matrix) {
		this.setValues(visible, alpha, shadow, compositeOperation, matrix);
		
	// public properties:
		// assigned in the setValues method.
		/**
		 * Property representing the alpha that will be applied to a display object.
		 * @property alpha
		 * @type Number
		 **/
	
		/**
		 * Property representing the shadow that will be applied to a display object.
		 * @property shadow
		 * @type Shadow
		 **/
	
		/**
		 * Property representing the compositeOperation that will be applied to a display object.
		 * You can find a list of valid composite operations at:
		 * <a href="https://developer.mozilla.org/en/Canvas_tutorial/Compositing">https://developer.mozilla.org/en/Canvas_tutorial/Compositing</a>
		 * @property compositeOperation
		 * @type String
		 **/
		
		/**
		 * Property representing the value for visible that will be applied to a display object.
		 * @property visible
		 * @type Boolean
		 **/
		
		/**
		 * The transformation matrix that will be applied to a display object.
		 * @property matrix
		 * @type Matrix2D
		 **/
	}
	var p = DisplayProps.prototype;

// initialization:
	/**
	 * Reinitializes the instance with the specified values.
	 * @method setValues
	 * @param {Number} [visible=true] Visible value.
	 * @param {Number} [alpha=1] Alpha value.
	 * @param {Number} [shadow=null] A Shadow instance or null.
	 * @param {Number} [compositeOperation=null] A compositeOperation value or null.
	 * @param {Number} [matrix] A transformation matrix. Defaults to an identity matrix.
	 * @return {DisplayProps} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.setValues = function (visible, alpha, shadow, compositeOperation, matrix) {
		this.visible = visible == null ? true : !!visible;
		this.alpha = alpha == null ? 1 : alpha;
		this.shadow = shadow;
		this.compositeOperation = compositeOperation;
		this.matrix = matrix || (this.matrix&&this.matrix.identity()) || new createjs.Matrix2D();
		return this;
	};

// public methods:
	/**
	 * Appends the specified display properties. This is generally used to apply a child's properties its parent's.
	 * @method append
	 * @param {Boolean} visible desired visible value
	 * @param {Number} alpha desired alpha value
	 * @param {Shadow} shadow desired shadow value
	 * @param {String} compositeOperation desired composite operation value
	 * @param {Matrix2D} [matrix] a Matrix2D instance
	 * @return {DisplayProps} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.append = function(visible, alpha, shadow, compositeOperation, matrix) {
		this.alpha *= alpha;
		this.shadow = shadow || this.shadow;
		this.compositeOperation = compositeOperation || this.compositeOperation;
		this.visible = this.visible && visible;
		matrix&&this.matrix.appendMatrix(matrix);
		return this;
	};
	
	/**
	 * Prepends the specified display properties. This is generally used to apply a parent's properties to a child's.
	 * For example, to get the combined display properties that would be applied to a child, you could use:
	 * 
	 * 	var o = myDisplayObject;
	 * 	var props = new createjs.DisplayProps();
	 * 	do {
	 * 		// prepend each parent's props in turn:
	 * 		props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation, o.getMatrix());
	 * 	} while (o = o.parent);
	 * 	
	 * @method prepend
	 * @param {Boolean} visible desired visible value
	 * @param {Number} alpha desired alpha value
	 * @param {Shadow} shadow desired shadow value
	 * @param {String} compositeOperation desired composite operation value
	 * @param {Matrix2D} [matrix] a Matrix2D instance
	 * @return {DisplayProps} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.prepend = function(visible, alpha, shadow, compositeOperation, matrix) {
		this.alpha *= alpha;
		this.shadow = this.shadow || shadow;
		this.compositeOperation = this.compositeOperation || compositeOperation;
		this.visible = this.visible && visible;
		matrix&&this.matrix.prependMatrix(matrix);
		return this;
	};
	
	/**
	 * Resets this instance and its matrix to default values.
	 * @method identity
	 * @return {DisplayProps} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.identity = function() {
		this.visible = true;
		this.alpha = 1;
		this.shadow = this.compositeOperation = null;
		this.matrix.identity();
		return this;
	};
	
	/**
	 * Returns a clone of the DisplayProps instance. Clones the associated matrix.
	 * @method clone
	 * @return {DisplayProps} a clone of the DisplayProps instance.
	 **/
	p.clone = function() {
		return new DisplayProps(this.alpha, this.shadow, this.compositeOperation, this.visible, this.matrix.clone());
	};

// private methods:

	createjs.DisplayProps = DisplayProps;
})();

//##############################################################################
// Point.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Represents a point on a 2 dimensional x / y coordinate system.
	 *
	 * <h4>Example</h4>
	 * 
	 *      var point = new createjs.Point(0, 100);
	 * 
	 * @class Point
	 * @param {Number} [x=0] X position.
	 * @param {Number} [y=0] Y position.
	 * @constructor
	 **/
	function Point(x, y) {
	 	this.setValues(x, y);
	 	
	 	
	// public properties:
		// assigned in the setValues method.
		/**
		 * X position.
		 * @property x
		 * @type Number
		 **/
	
		/**
		 * Y position.
		 * @property y
		 * @type Number
		 **/
	}
	var p = Point.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.

	
// public methods:
	/** 
	 * Sets the specified values on this instance.
	 * @method setValues
	 * @param {Number} [x=0] X position.
	 * @param {Number} [y=0] Y position.
	 * @return {Point} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.setValues = function(x, y) {
		this.x = x||0;
		this.y = y||0;
		return this;
	};
	
	/**
	 * Copies all properties from the specified point to this point.
	 * @method copy
	 * @param {Point} point The point to copy properties from.
	 * @return {Point} This point. Useful for chaining method calls.
	 * @chainable
	*/
	p.copy = function(point) {
		this.x = point.x;
		this.y = point.y;
		return this;
	};
	
	/**
	 * Returns a clone of the Point instance.
	 * @method clone
	 * @return {Point} a clone of the Point instance.
	 **/
	p.clone = function() {
		return new Point(this.x, this.y);
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Point (x="+this.x+" y="+this.y+")]";
	};
	
	
	createjs.Point = Point;
}());

//##############################################################################
// Rectangle.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Represents a rectangle as defined by the points (x, y) and (x+width, y+height).
	 *
	 * <h4>Example</h4>
	 *
	 *      var rect = new createjs.Rectangle(0, 0, 100, 100);
	 *
	 * @class Rectangle
	 * @param {Number} [x=0] X position.
	 * @param {Number} [y=0] Y position.
	 * @param {Number} [width=0] The width of the Rectangle.
	 * @param {Number} [height=0] The height of the Rectangle.
	 * @constructor
	 **/
	function Rectangle(x, y, width, height) {
		this.setValues(x, y, width, height);
		
		
	// public properties:
		// assigned in the setValues method.
		/**
		 * X position.
		 * @property x
		 * @type Number
		 **/
	
		/**
		 * Y position.
		 * @property y
		 * @type Number
		 **/
	
		/**
		 * Width.
		 * @property width
		 * @type Number
		 **/
	
		/**
		 * Height.
		 * @property height
		 * @type Number
		 **/
	}
	var p = Rectangle.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// public methods:
	/** 
	 * Sets the specified values on this instance.
	 * @method setValues
	 * @param {Number} [x=0] X position.
	 * @param {Number} [y=0] Y position.
	 * @param {Number} [width=0] The width of the Rectangle.
	 * @param {Number} [height=0] The height of the Rectangle.
	 * @return {Rectangle} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.setValues = function(x, y, width, height) {
		// don't forget to update docs in the constructor if these change:
		this.x = x||0;
		this.y = y||0;
		this.width = width||0;
		this.height = height||0;
		return this;
	};
	
	/** 
	 * Extends the rectangle's bounds to include the described point or rectangle.
	 * @method extend
	 * @param {Number} x X position of the point or rectangle.
	 * @param {Number} y Y position of the point or rectangle.
	 * @param {Number} [width=0] The width of the rectangle.
	 * @param {Number} [height=0] The height of the rectangle.
	 * @return {Rectangle} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.extend = function(x, y, width, height) {
		width = width||0;
		height = height||0;
		if (x+width > this.x+this.width) { this.width = x+width-this.x; }
		if (y+height > this.y+this.height) { this.height = y+height-this.y; }
		if (x < this.x) { this.width += this.x-x; this.x = x; }
		if (y < this.y) { this.height += this.y-y; this.y = y; }
		return this;
	};
	
	/** 
	 * Adds the specified padding to the rectangle's bounds.
	 * @method pad
	 * @param {Number} top
	 * @param {Number} left
	 * @param {Number} right
	 * @param {Number} bottom
	 * @return {Rectangle} This instance. Useful for chaining method calls.
	 * @chainable
	*/
	p.pad = function(top, left, bottom, right) {
		this.x -= left;
		this.y -= top;
		this.width += left+right;
		this.height += top+bottom;
		return this;
	};
	
	/**
	 * Copies all properties from the specified rectangle to this rectangle.
	 * @method copy
	 * @param {Rectangle} rectangle The rectangle to copy properties from.
	 * @return {Rectangle} This rectangle. Useful for chaining method calls.
	 * @chainable
	*/
	p.copy = function(rectangle) {
		return this.setValues(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
	};
	
	/** 
	 * Returns true if this rectangle fully encloses the described point or rectangle.
	 * @method contains
	 * @param {Number} x X position of the point or rectangle.
	 * @param {Number} y Y position of the point or rectangle.
	 * @param {Number} [width=0] The width of the rectangle.
	 * @param {Number} [height=0] The height of the rectangle.
	 * @return {Boolean} True if the described point or rectangle is contained within this rectangle.
	*/
	p.contains = function(x, y, width, height) {
		width = width||0;
		height = height||0;
		return (x >= this.x && x+width <= this.x+this.width && y >= this.y && y+height <= this.y+this.height);
	};
	
	/** 
	 * Returns a new rectangle which contains this rectangle and the specified rectangle.
	 * @method union
	 * @param {Rectangle} rect The rectangle to calculate a union with.
	 * @return {Rectangle} A new rectangle describing the union.
	*/
	p.union = function(rect) {
		return this.clone().extend(rect.x, rect.y, rect.width, rect.height);
	};
	
	/** 
	 * Returns a new rectangle which describes the intersection (overlap) of this rectangle and the specified rectangle,
	 * or null if they do not intersect.
	 * @method intersection
	 * @param {Rectangle} rect The rectangle to calculate an intersection with.
	 * @return {Rectangle} A new rectangle describing the intersection or null.
	*/
	p.intersection = function(rect) {
		var x1 = rect.x, y1 = rect.y, x2 = x1+rect.width, y2 = y1+rect.height;
		if (this.x > x1) { x1 = this.x; }
		if (this.y > y1) { y1 = this.y; }
		if (this.x + this.width < x2) { x2 = this.x + this.width; }
		if (this.y + this.height < y2) { y2 = this.y + this.height; }
		return (x2 <= x1 || y2 <= y1) ? null : new Rectangle(x1, y1, x2-x1, y2-y1);
	};
	
	/** 
	 * Returns true if the specified rectangle intersects (has any overlap) with this rectangle.
	 * @method intersects
	 * @param {Rectangle} rect The rectangle to compare.
	 * @return {Boolean} True if the rectangles intersect.
	*/
	p.intersects = function(rect) {
		return (rect.x <= this.x+this.width && this.x <= rect.x+rect.width && rect.y <= this.y+this.height && this.y <= rect.y + rect.height);
	};
	
	/** 
	 * Returns true if the width or height are equal or less than 0.
	 * @method isEmpty
	 * @return {Boolean} True if the rectangle is empty.
	*/
	p.isEmpty = function() {
		return this.width <= 0 || this.height <= 0;
	};
	
	/**
	 * Returns a clone of the Rectangle instance.
	 * @method clone
	 * @return {Rectangle} a clone of the Rectangle instance.
	 **/
	p.clone = function() {
		return new Rectangle(this.x, this.y, this.width, this.height);
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Rectangle (x="+this.x+" y="+this.y+" width="+this.width+" height="+this.height+")]";
	};
	
	
	createjs.Rectangle = Rectangle;
}());

//##############################################################################
// ButtonHelper.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * The ButtonHelper is a helper class to create interactive buttons from {{#crossLink "MovieClip"}}{{/crossLink}} or
	 * {{#crossLink "Sprite"}}{{/crossLink}} instances. This class will intercept mouse events from an object, and
	 * automatically call {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}},
	 * to the respective animation labels, add a pointer cursor, and allows the user to define a hit state frame.
	 *
	 * The ButtonHelper instance does not need to be added to the stage, but a reference should be maintained to prevent
	 * garbage collection.
	 * 
	 * Note that over states will not work unless you call {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 *
	 *      var helper = new createjs.ButtonHelper(myInstance, "out", "over", "down", false, myInstance, "hit");
	 *      myInstance.addEventListener("click", handleClick);
	 *      function handleClick(event) {
	 *          // Click Happened.
	 *      }
	 *
	 * @class ButtonHelper
	 * @param {Sprite|MovieClip} target The instance to manage.
	 * @param {String} [outLabel="out"] The label or animation to go to when the user rolls out of the button.
	 * @param {String} [overLabel="over"] The label or animation to go to when the user rolls over the button.
	 * @param {String} [downLabel="down"] The label or animation to go to when the user presses the button.
	 * @param {Boolean} [play=false] If the helper should call "gotoAndPlay" or "gotoAndStop" on the button when changing
	 * states.
	 * @param {DisplayObject} [hitArea] An optional item to use as the hit state for the button. If this is not defined,
	 * then the button's visible states will be used instead. Note that the same instance as the "target" argument can be
	 * used for the hitState.
	 * @param {String} [hitLabel] The label or animation on the hitArea instance that defines the hitArea bounds. If this is
	 * null, then the default state of the hitArea will be used. *
	 * @constructor
	 */
	function ButtonHelper(target, outLabel, overLabel, downLabel, play, hitArea, hitLabel) {
		if (!target.addEventListener) { return; }
	
	
	// public properties:
		/**
		 * The target for this button helper.
		 * @property target
		 * @type MovieClip | Sprite
		 * @readonly
		 **/
		this.target = target;
	
		/**
		 * The label name or frame number to display when the user mouses out of the target. Defaults to "over".
		 * @property overLabel
		 * @type String | Number
		 **/
		this.overLabel = overLabel == null ? "over" : overLabel;
	
		/**
		 * The label name or frame number to display when the user mouses over the target. Defaults to "out".
		 * @property outLabel
		 * @type String | Number
		 **/
		this.outLabel = outLabel == null ? "out" : outLabel;
	
		/**
		 * The label name or frame number to display when the user presses on the target. Defaults to "down".
		 * @property downLabel
		 * @type String | Number
		 **/
		this.downLabel = downLabel == null ? "down" : downLabel;
	
		/**
		 * If true, then ButtonHelper will call gotoAndPlay, if false, it will use gotoAndStop. Default is false.
		 * @property play
		 * @default false
		 * @type Boolean
		 **/
		this.play = play;
		
		
	//  private properties
		/**
		 * @property _isPressed
		 * @type Boolean
		 * @protected
		 **/
		this._isPressed = false;
	
		/**
		 * @property _isOver
		 * @type Boolean
		 * @protected
		 **/
		this._isOver = false;
	
		/**
		 * @property _enabled
		 * @type Boolean
		 * @protected
		 **/
		this._enabled = false;
		
	// setup:
		target.mouseChildren = false; // prevents issues when children are removed from the display list when state changes.
		this.enabled = true;
		this.handleEvent({});
		if (hitArea) {
			if (hitLabel) {
				hitArea.actionsEnabled = false;
				hitArea.gotoAndStop&&hitArea.gotoAndStop(hitLabel);
			}
			target.hitArea = hitArea;
		}
	}
	var p = ButtonHelper.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.

	
// getter / setters:
	/**
	 * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
	 * @method setEnabled
	 * @param {Boolean} value
	 * @deprecated
	 **/
	p.setEnabled = function(value) { // TODO: deprecated.
		if (value == this._enabled) { return; }
		var o = this.target;
		this._enabled = value;
		if (value) {
			o.cursor = "pointer";
			o.addEventListener("rollover", this);
			o.addEventListener("rollout", this);
			o.addEventListener("mousedown", this);
			o.addEventListener("pressup", this);
			if (o._reset) { o.__reset = o._reset; o._reset = this._reset;}
		} else {
			o.cursor = null;
			o.removeEventListener("rollover", this);
			o.removeEventListener("rollout", this);
			o.removeEventListener("mousedown", this);
			o.removeEventListener("pressup", this);
			if (o.__reset) { o._reset = o.__reset; delete(o.__reset); }
		}
	};
	/**
	 * Use the {{#crossLink "ButtonHelper/enabled:property"}}{{/crossLink}} property instead.
	 * @method getEnabled
	 * @return {Boolean}
	 * @deprecated
	 **/
	p.getEnabled = function() {
		return this._enabled;
	};

	/**
	 * Enables or disables the button functionality on the target.
	 * @property enabled
	 * @type {Boolean}
	 **/
	try {
		Object.defineProperties(p, {
			enabled: { get: p.getEnabled, set: p.setEnabled }
		});
	} catch (e) {} // TODO: use Log


// public methods:
	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[ButtonHelper]";
	};


// private methods:
	/**
	 * @method handleEvent
	 * @param {Object} evt The mouse event to handle.
	 * @protected
	 **/
	p.handleEvent = function(evt) {
		var label, t = this.target, type = evt.type;
		if (type == "mousedown") {
			this._isPressed = true;
			label = this.downLabel;
		} else if (type == "pressup") {
			this._isPressed = false;
			label = this._isOver ? this.overLabel : this.outLabel;
		} else if (type == "rollover") {
			this._isOver = true;
			label = this._isPressed ? this.downLabel : this.overLabel;
		} else { // rollout and default
			this._isOver = false;
			label = this._isPressed ? this.overLabel : this.outLabel;
		}
		if (this.play) {
			t.gotoAndPlay&&t.gotoAndPlay(label);
		} else {
			t.gotoAndStop&&t.gotoAndStop(label);
		}
	};
	
	/**
	 * Injected into target. Preserves the paused state through a reset.
	 * @method _reset
	 * @protected
	 **/
	p._reset = function() {
		// TODO: explore better ways to handle this issue. This is hacky & disrupts object signatures.
		var p = this.paused;
		this.__reset();
		this.paused = p;
	};


	createjs.ButtonHelper = ButtonHelper;
}());

//##############################################################################
// Shadow.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * This class encapsulates the properties required to define a shadow to apply to a {{#crossLink "DisplayObject"}}{{/crossLink}}
	 * via its <code>shadow</code> property.
	 *
	 * <h4>Example</h4>
	 *
	 *      myImage.shadow = new createjs.Shadow("#000000", 5, 5, 10);
	 *
	 * @class Shadow
	 * @constructor
	 * @param {String} color The color of the shadow. This can be any valid CSS color value.
	 * @param {Number} offsetX The x offset of the shadow in pixels.
	 * @param {Number} offsetY The y offset of the shadow in pixels.
	 * @param {Number} blur The size of the blurring effect.
	 **/
	function Shadow(color, offsetX, offsetY, blur) {
		
		
	// public properties:
		/** 
		 * The color of the shadow. This can be any valid CSS color value.
		 * @property color
		 * @type String
		 * @default null
		 */
		this.color = color||"black";
	
		/** The x offset of the shadow.
		 * @property offsetX
		 * @type Number
		 * @default 0
		 */
		this.offsetX = offsetX||0;
	
		/** The y offset of the shadow.
		 * @property offsetY
		 * @type Number
		 * @default 0
		 */
		this.offsetY = offsetY||0;
	
		/** The blur of the shadow.
		 * @property blur
		 * @type Number
		 * @default 0
		 */
		this.blur = blur||0;
	}
	var p = Shadow.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// static public properties:
	/**
	 * An identity shadow object (all properties are set to 0).
	 * @property identity
	 * @type Shadow
	 * @static
	 * @final
	 * @readonly
	 **/
	Shadow.identity = new Shadow("transparent", 0, 0, 0);


// public methods:
	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Shadow]";
	};

	/**
	 * Returns a clone of this Shadow instance.
	 * @method clone
	 * @return {Shadow} A clone of the current Shadow instance.
	 **/
	p.clone = function() {
		return new Shadow(this.color, this.offsetX, this.offsetY, this.blur);
	};
	

	createjs.Shadow = Shadow;
}());

//##############################################################################
// SpriteSheet.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Encapsulates the properties and methods associated with a sprite sheet. A sprite sheet is a series of images (usually
	 * animation frames) combined into a larger image (or images). For example, an animation consisting of eight 100x100
	 * images could be combined into a single 400x200 sprite sheet (4 frames across by 2 high).
	 *
	 * The data passed to the SpriteSheet constructor defines:
	 * <ol>
	 * 	<li> The source image or images to use.</li>
	 * 	<li> The positions of individual image frames.</li>
	 * 	<li> Sequences of frames that form named animations. Optional.</li>
	 * 	<li> The target playback framerate. Optional.</li>
	 * </ol>
	 * <h3>SpriteSheet Format</h3>
	 * SpriteSheets are an object with two required properties (`images` and `frames`), and two optional properties
	 * (`framerate` and `animations`). This makes them easy to define in javascript code, or in JSON.
	 *
	 * <h4>images</h4>
	 * An array of source images. Images can be either an HTMlimage
	 * instance, or a uri to an image. The former is recommended to control preloading.
	 *
	 * 	images: [image1, "path/to/image2.png"],
	 *
	 * <h4>frames</h4>
	 * Defines the individual frames. There are two supported formats for frame data:
	 * When all of the frames are the same size (in a grid), use an object with `width`, `height`, `regX`, `regY`,
	 * and `count` properties.
	 *
	 * <ul>
	 *  <li>`width` & `height` are required and specify the dimensions of the frames</li>
	 *  <li>`regX` & `regY` indicate the registration point or "origin" of the frames</li>
	 *  <li>`spacing` indicate the spacing between frames</li>
	 *  <li>`margin` specify the margin around the image(s)</li>
	 *  <li>`count` allows you to specify the total number of frames in the spritesheet; if omitted, this will
	 *  be calculated based on the dimensions of the source images and the frames. Frames will be assigned
	 *  indexes based on their position in the source images (left to right, top to bottom).</li>
	 * </ul>
	 *
	 *  	frames: {width:64, height:64, count:20, regX: 32, regY:64, spacing:0, margin:0}
	 *
	 * If the frames are of different sizes, use an array of frame definitions. Each definition is itself an array
	 * with 4 required and 3 optional entries, in the order:
	 *
	 * <ul>
	 *  <li>The first four, `x`, `y`, `width`, and `height` are required and define the frame rectangle.</li>
	 *  <li>The fifth, `imageIndex`, specifies the index of the source image (defaults to 0)</li>
	 *  <li>The last two, `regX` and `regY` specify the registration point of the frame</li>
	 * </ul>
	 *
	 * 	frames: [
	 * 		// x, y, width, height, imageIndex*, regX*, regY*
	 * 		[64, 0, 96, 64],
	 * 		[0, 0, 64, 64, 1, 32, 32]
	 * 		// etc.
	 * 	]
	 *
	 * <h4>animations</h4>
	 * Optional. An object defining sequences of frames to play as named animations. Each property corresponds to an
	 * animation of the same name. Each animation must specify the frames to play, and may
	 * also include a relative playback `speed` (ex. 2 would playback at double speed, 0.5 at half), and
	 * the name of the `next` animation to sequence to after it completes.
	 *
	 * There are three formats supported for defining the frames in an animation, which can be mixed and matched as appropriate:
	 * <ol>
	 * 	<li>for a single frame animation, you can simply specify the frame index
	 *
	 * 		animations: {
	 * 			sit: 7
	 * 		}
	 *
	 * </li>
	 * <li>
	 *      for an animation of consecutive frames, you can use an array with two required, and two optional entries
	 * 		in the order: `start`, `end`, `next`, and `speed`. This will play the frames from start to end inclusive.
	 *
	 * 		animations: {
	 * 			// start, end, next*, speed*
	 * 			run: [0, 8],
	 * 			jump: [9, 12, "run", 2]
	 * 		}
	 *
	 *  </li>
	 *  <li>
	 *     for non-consecutive frames, you can use an object with a `frames` property defining an array of frame
	 *     indexes to play in order. The object can also specify `next` and `speed` properties.
	 *
	 * 		animations: {
	 * 			walk: {
	 * 				frames: [1,2,3,3,2,1]
	 * 			},
	 * 			shoot: {
	 * 				frames: [1,4,5,6],
	 * 				next: "walk",
	 * 				speed: 0.5
	 * 			}
	 * 		}
	 *
	 *  </li>
	 * </ol>
	 * <strong>Note:</strong> the `speed` property was added in EaselJS 0.7.0. Earlier versions had a `frequency`
	 * property instead, which was the inverse of `speed`. For example, a value of "4" would be 1/4 normal speed in
	 * earlier versions, but is 4x normal speed in EaselJS 0.7.0+.
	 *
	 * <h4>framerate</h4>
	 * Optional. Indicates the default framerate to play this spritesheet at in frames per second. See
	 * {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} for more information.
	 *
	 * 		framerate: 20
	 *
	 * Note that the Sprite framerate will only work if the stage update method is provided with the {{#crossLink "Ticker/tick:event"}}{{/crossLink}}
	 * event generated by the {{#crossLink "Ticker"}}{{/crossLink}}.
	 *
	 * 		createjs.Ticker.on("tick", handleTick);
	 * 		function handleTick(event) {
	 *			stage.update(event);
	 *		}
	 *
	 * <h3>Example</h3>
	 * To define a simple sprite sheet, with a single image "sprites.jpg" arranged in a regular 50x50 grid with three
	 * animations: "stand" showing the first frame, "run" looping frame 1-5 inclusive, and "jump" playing frame 6-8 and
	 * sequencing back to run.
	 *
	 * 		var data = {
	 * 			images: ["sprites.jpg"],
	 * 			frames: {width:50, height:50},
	 * 			animations: {
	 * 				stand:0,
	 * 				run:[1,5],
	 * 				jump:[6,8,"run"]
	 * 			}
	 * 		};
	 * 		var spriteSheet = new createjs.SpriteSheet(data);
	 * 		var animation = new createjs.Sprite(spriteSheet, "run");
	 *
	 * <h3>Generating SpriteSheet Images</h3>
	 * Spritesheets can be created manually by combining images in PhotoShop, and specifying the frame size or
	 * coordinates manually, however there are a number of tools that facilitate this.
	 * <ul>
	 *     <li>Exporting SpriteSheets or HTML5 content from Flash Pro supports the EaselJS SpriteSheet format.</li>
	 *     <li>The popular <a href="https://www.codeandweb.com/texturepacker/easeljs" target="_blank">Texture Packer</a> has
	 *     EaselJS support.
	 *     <li>SWF animations in Flash can be exported to SpriteSheets using <a href="http://createjs.com/zoe" target="_blank"></a></li>
	 * </ul>
	 *
	 * <h3>Cross Origin Issues</h3>
	 * <strong>Warning:</strong> Images loaded cross-origin will throw cross-origin security errors when interacted with
	 * using:
	 * <ul>
	 *     <li>a mouse</li>
	 *     <li>methods such as {{#crossLink "Container/getObjectUnderPoint"}}{{/crossLink}}</li>
	 *     <li>Filters (see {{#crossLink "Filter"}}{{/crossLink}})</li>
	 *     <li>caching (see {{#crossLink "DisplayObject/cache"}}{{/crossLink}})</li>
	 * </ul>
	 * You can get around this by setting `crossOrigin` property on your images before passing them to EaselJS, or
	 * setting the `crossOrigin` property on PreloadJS' LoadQueue or LoadItems.
	 *
	 * 		var image = new Image();
	 * 		img.crossOrigin="Anonymous";
	 * 		img.src = "http://server-with-CORS-support.com/path/to/image.jpg";
	 *
	 * If you pass string paths to SpriteSheets, they will not work cross-origin. The server that stores the image must
	 * support cross-origin requests, or this will not work. For more information, check out
	 * <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS" target="_blank">CORS overview on MDN</a>.
	 *
	 * @class SpriteSheet
	 * @constructor
	 * @param {Object} data An object describing the SpriteSheet data.
	 * @extends EventDispatcher
	 **/
	function SpriteSheet(data) {
		this.EventDispatcher_constructor();


		// public properties:
		/**
		 * Indicates whether all images are finished loading.
		 * @property complete
		 * @type Boolean
		 * @readonly
		 **/
		this.complete = true;

		/**
		 * Specifies the framerate to use by default for Sprite instances using the SpriteSheet. See the Sprite class
		 * {{#crossLink "Sprite/framerate:property"}}{{/crossLink}} for more information.
		 * @property framerate
		 * @type Number
		 **/
		this.framerate = 0;


		// private properties:
		/**
		 * @property _animations
		 * @protected
		 * @type Array
		 **/
		this._animations = null;

		/**
		 * @property _frames
		 * @protected
		 * @type Array
		 **/
		this._frames = null;

		/**
		 * @property _images
		 * @protected
		 * @type Array
		 **/
		this._images = null;

		/**
		 * @property _data
		 * @protected
		 * @type Object
		 **/
		this._data = null;

		/**
		 * @property _loadCount
		 * @protected
		 * @type Number
		 **/
		this._loadCount = 0;

		// only used for simple frame defs:
		/**
		 * @property _frameHeight
		 * @protected
		 * @type Number
		 **/
		this._frameHeight = 0;

		/**
		 * @property _frameWidth
		 * @protected
		 * @type Number
		 **/
		this._frameWidth = 0;

		/**
		 * @property _numFrames
		 * @protected
		 * @type Number
		 **/
		this._numFrames = 0;

		/**
		 * @property _regX
		 * @protected
		 * @type Number
		 **/
		this._regX = 0;

		/**
		 * @property _regY
		 * @protected
		 * @type Number
		 **/
		this._regY = 0;

		/**
		 * @property _spacing
		 * @protected
		 * @type Number
		 **/
		this._spacing = 0;

		/**
		 * @property _margin
		 * @protected
		 * @type Number
		 **/
		this._margin = 0;

		// setup:
		this._parseData(data);
	}
	var p = createjs.extend(SpriteSheet, createjs.EventDispatcher);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// events:
	/**
	 * Dispatched when all images are loaded.  Note that this only fires if the images
	 * were not fully loaded when the sprite sheet was initialized. You should check the complete property
	 * to prior to adding a listener. Ex.
	 *
	 * 	var sheet = new createjs.SpriteSheet(data);
	 * 	if (!sheet.complete) {
	 * 		// not preloaded, listen for the complete event:
	 * 		sheet.addEventListener("complete", handler);
	 * 	}
	 *
	 * @event complete
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @since 0.6.0
	 */

	/**
	 * Dispatched when getFrame is called with a valid frame index. This is primarily intended for use by {{#crossLink "SpriteSheetBuilder"}}{{/crossLink}}
	 * when doing on-demand rendering.
	 * @event getframe
	 * @param {Number} index The frame index.
	 * @param {Object} frame The frame object that getFrame will return.
	 */

	/**
	 * Dispatched when an image encounters an error. A SpriteSheet will dispatch an error event for each image that
	 * encounters an error, and will still dispatch a {{#crossLink "SpriteSheet/complete:event"}}{{/crossLink}}
	 * event once all images are finished processing, even if an error is encountered.
	 * @event error
	 * @param {String} src The source of the image that failed to load.
	 * @since 0.8.2
	 */


// getter / setters:
	/**
	 * Use the {{#crossLink "SpriteSheet/animations:property"}}{{/crossLink}} property instead.
	 * @method getAnimations
	 * @return {Array}
	 * @deprecated
	 **/
	p.getAnimations = function() {
		return this._animations.slice();
	};

	/**
	 * Returns an array of all available animation names available on this sprite sheet as strings.
	 * @property animations
	 * @type {Array}
	 * @readonly
	 **/
	try {
		Object.defineProperties(p, {
			animations: { get: p.getAnimations }
		});
	} catch (e) {}


// public methods:
	/**
	 * Returns the total number of frames in the specified animation, or in the whole sprite
	 * sheet if the animation param is omitted. Returns 0 if the spritesheet relies on calculated frame counts, and
	 * the images have not been fully loaded.
	 * @method getNumFrames
	 * @param {String} animation The name of the animation to get a frame count for.
	 * @return {Number} The number of frames in the animation, or in the entire sprite sheet if the animation param is omitted.
	 */
	p.getNumFrames = function(animation) {
		if (animation == null) {
			return this._frames ? this._frames.length : this._numFrames || 0;
		} else {
			var data = this._data[animation];
			if (data == null) { return 0; }
			else { return data.frames.length; }
		}
	};

	/**
	 * Returns an object defining the specified animation. The returned object contains:<UL>
	 * 	<li>frames: an array of the frame ids in the animation</li>
	 * 	<li>speed: the playback speed for this animation</li>
	 * 	<li>name: the name of the animation</li>
	 * 	<li>next: the default animation to play next. If the animation loops, the name and next property will be the
	 * 	same.</li>
	 * </UL>
	 * @method getAnimation
	 * @param {String} name The name of the animation to get.
	 * @return {Object} a generic object with frames, speed, name, and next properties.
	 **/
	p.getAnimation = function(name) {
		return this._data[name];
	};

	/**
	 * Returns an object specifying the image and source rect of the specified frame. The returned object has:<UL>
	 * 	<li>an image property holding a reference to the image object in which the frame is found</li>
	 * 	<li>a rect property containing a Rectangle instance which defines the boundaries for the frame within that
	 * 	image.</li>
	 * 	<li> A regX and regY property corresponding to the regX/Y values for the frame.
	 * </UL>
	 * @method getFrame
	 * @param {Number} frameIndex The index of the frame.
	 * @return {Object} a generic object with image and rect properties. Returns null if the frame does not exist.
	 **/
	p.getFrame = function(frameIndex) {
		var frame;
		if (this._frames && (frame=this._frames[frameIndex])) { return frame; }
		return null;
	};

	/**
	 * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the specified frame relative
	 * to the origin. For example, a 90 x 70 frame with a regX of 50 and a regY of 40 would return:
	 *
	 * 	[x=-50, y=-40, width=90, height=70]
	 *
	 * @method getFrameBounds
	 * @param {Number} frameIndex The index of the frame.
	 * @param {Rectangle} [rectangle] A Rectangle instance to copy the values into. By default a new instance is created.
	 * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully loaded.
	 **/
	p.getFrameBounds = function(frameIndex, rectangle) {
		var frame = this.getFrame(frameIndex);
		return frame ? (rectangle||new createjs.Rectangle()).setValues(-frame.regX, -frame.regY, frame.rect.width, frame.rect.height) : null;
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[SpriteSheet]";
	};

	/**
	 * SpriteSheet cannot be cloned. A SpriteSheet can be shared by multiple Sprite instances without cloning it.
	 * @method clone
	 **/
	p.clone = function() {
		throw("SpriteSheet cannot be cloned.")
	};

// private methods:
	/**
	 * @method _parseData
	 * @param {Object} data An object describing the SpriteSheet data.
	 * @protected
	 **/
	p._parseData = function(data) {
		var i,l,o,a;
		if (data == null) { return; }

		this.framerate = data.framerate||0;

		// parse images:
		if (data.images && (l=data.images.length) > 0) {
			a = this._images = [];
			for (i=0; i<l; i++) {
				var img = data.images[i];
				if (typeof img == "string") {
					var src = img;
					img = document.createElement("img");
					img.src = src;
				}
				a.push(img);
				if (!img.getContext && !img.naturalWidth) {
					this._loadCount++;
					this.complete = false;
					(function(o, src) { img.onload = function() { o._handleImageLoad(src); } })(this, src);
					(function(o, src) { img.onerror = function() { o._handleImageError(src); } })(this, src);
				}
			}
		}

		// parse frames:
		if (data.frames == null) { // nothing
		} else if (Array.isArray(data.frames)) {
			this._frames = [];
			a = data.frames;
			for (i=0,l=a.length;i<l;i++) {
				var arr = a[i];
				this._frames.push({image:this._images[arr[4]?arr[4]:0], rect:new createjs.Rectangle(arr[0],arr[1],arr[2],arr[3]), regX:arr[5]||0, regY:arr[6]||0 });
			}
		} else {
			o = data.frames;
			this._frameWidth = o.width;
			this._frameHeight = o.height;
			this._regX = o.regX||0;
			this._regY = o.regY||0;
			this._spacing = o.spacing||0;
			this._margin = o.margin||0;
			this._numFrames = o.count;
			if (this._loadCount == 0) { this._calculateFrames(); }
		}

		// parse animations:
		this._animations = [];
		if ((o=data.animations) != null) {
			this._data = {};
			var name;
			for (name in o) {
				var anim = {name:name};
				var obj = o[name];
				if (typeof obj == "number") { // single frame
					a = anim.frames = [obj];
				} else if (Array.isArray(obj)) { // simple
					if (obj.length == 1) { anim.frames = [obj[0]]; }
					else {
						anim.speed = obj[3];
						anim.next = obj[2];
						a = anim.frames = [];
						for (i=obj[0];i<=obj[1];i++) {
							a.push(i);
						}
					}
				} else { // complex
					anim.speed = obj.speed;
					anim.next = obj.next;
					var frames = obj.frames;
					a = anim.frames = (typeof frames == "number") ? [frames] : frames.slice(0);
				}
				if (anim.next === true || anim.next === undefined) { anim.next = name; } // loop
				if (anim.next === false || (a.length < 2 && anim.next == name)) { anim.next = null; } // stop
				if (!anim.speed) { anim.speed = 1; }
				this._animations.push(name);
				this._data[name] = anim;
			}
		}
	};

	/**
	 * @method _handleImageLoad
	 * @protected
	 **/
	p._handleImageLoad = function(src) {
		if (--this._loadCount == 0) {
			this._calculateFrames();
			this.complete = true;
			this.dispatchEvent("complete");
		}
	};

	/**
	 * @method _handleImageError
	 * @protected
	 */
	p._handleImageError = function (src) {
		var errorEvent = new createjs.Event("error");
		errorEvent.src = src;
		this.dispatchEvent(errorEvent);

		// Complete is still dispatched.
		if (--this._loadCount == 0) {
			this.dispatchEvent("complete");
		}
	};

	/**
	 * @method _calculateFrames
	 * @protected
	 **/
	p._calculateFrames = function() {
		if (this._frames || this._frameWidth == 0) { return; }

		this._frames = [];

		var maxFrames = this._numFrames || 100000; // if we go over this, something is wrong.
		var frameCount = 0, frameWidth = this._frameWidth, frameHeight = this._frameHeight;
		var spacing = this._spacing, margin = this._margin;
		
		imgLoop:
		for (var i=0, imgs=this._images; i<imgs.length; i++) {
			var img = imgs[i], imgW = img.width, imgH = img.height;

			var y = margin;
			while (y <= imgH-margin-frameHeight) {
				var x = margin;
				while (x <= imgW-margin-frameWidth) {
					if (frameCount >= maxFrames) { break imgLoop; }
					frameCount++;
					this._frames.push({
							image: img,
							rect: new createjs.Rectangle(x, y, frameWidth, frameHeight),
							regX: this._regX,
							regY: this._regY
						});
					x += frameWidth+spacing;
				}
				y += frameHeight+spacing;
			}
		}
		this._numFrames = frameCount;
	};


	createjs.SpriteSheet = createjs.promote(SpriteSheet, "EventDispatcher");
}());

//##############################################################################
// Graphics.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * The Graphics class exposes an easy to use API for generating vector drawing instructions and drawing them to a
	 * specified context. Note that you can use Graphics without any dependency on the EaselJS framework by calling {{#crossLink "Graphics/draw"}}{{/crossLink}}
	 * directly, or it can be used with the {{#crossLink "Shape"}}{{/crossLink}} object to draw vector graphics within the
	 * context of an EaselJS display list.
	 *
	 * There are two approaches to working with Graphics object: calling methods on a Graphics instance (the "Graphics API"), or
	 * instantiating Graphics command objects and adding them to the graphics queue via {{#crossLink "Graphics/append"}}{{/crossLink}}.
	 * The former abstracts the latter, simplifying beginning and ending paths, fills, and strokes.
	 *
	 *      var g = new createjs.Graphics();
	 *      g.setStrokeStyle(1);
	 *      g.beginStroke("#000000");
	 *      g.beginFill("red");
	 *      g.drawCircle(0,0,30);
	 *
	 * All drawing methods in Graphics return the Graphics instance, so they can be chained together. For example,
	 * the following line of code would generate the instructions to draw a rectangle with a red stroke and blue fill:
	 *
	 *      myGraphics.beginStroke("red").beginFill("blue").drawRect(20, 20, 100, 50);
	 *
	 * Each graphics API call generates a command object (see below). The last command to be created can be accessed via
	 * {{#crossLink "Graphics/command:property"}}{{/crossLink}}:
	 *
	 *      var fillCommand = myGraphics.beginFill("red").command;
	 *      // ... later, update the fill style/color:
	 *      fillCommand.style = "blue";
	 *      // or change it to a bitmap fill:
	 *      fillCommand.bitmap(myImage);
	 *
	 * For more direct control of rendering, you can instantiate and append command objects to the graphics queue directly. In this case, you
	 * need to manage path creation manually, and ensure that fill/stroke is applied to a defined path:
	 *
	 *      // start a new path. Graphics.beginCmd is a reusable BeginPath instance:
	 *      myGraphics.append(createjs.Graphics.beginCmd);
	 *      // we need to define the path before applying the fill:
	 *      var circle = new createjs.Graphics.Circle(0,0,30);
	 *      myGraphics.append(circle);
	 *      // fill the path we just defined:
	 *      var fill = new createjs.Graphics.Fill("red");
	 *      myGraphics.append(fill);
	 *
	 * These approaches can be used together, for example to insert a custom command:
	 *
	 *      myGraphics.beginFill("red");
	 *      var customCommand = new CustomSpiralCommand(etc);
	 *      myGraphics.append(customCommand);
	 *      myGraphics.beginFill("blue");
	 *      myGraphics.drawCircle(0, 0, 30);
	 *
	 * See {{#crossLink "Graphics/append"}}{{/crossLink}} for more info on creating custom commands.
	 *
	 * <h4>Tiny API</h4>
	 * The Graphics class also includes a "tiny API", which is one or two-letter methods that are shortcuts for all of the
	 * Graphics methods. These methods are great for creating compact instructions, and is used by the Toolkit for CreateJS
	 * to generate readable code. All tiny methods are marked as protected, so you can view them by enabling protected
	 * descriptions in the docs.
	 *
	 * <table>
	 *     <tr><td><b>Tiny</b></td><td><b>Method</b></td><td><b>Tiny</b></td><td><b>Method</b></td></tr>
	 *     <tr><td>mt</td><td>{{#crossLink "Graphics/moveTo"}}{{/crossLink}} </td>
	 *     <td>lt</td> <td>{{#crossLink "Graphics/lineTo"}}{{/crossLink}}</td></tr>
	 *     <tr><td>a/at</td><td>{{#crossLink "Graphics/arc"}}{{/crossLink}} / {{#crossLink "Graphics/arcTo"}}{{/crossLink}} </td>
	 *     <td>bt</td><td>{{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} </td></tr>
	 *     <tr><td>qt</td><td>{{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} (also curveTo)</td>
	 *     <td>r</td><td>{{#crossLink "Graphics/rect"}}{{/crossLink}} </td></tr>
	 *     <tr><td>cp</td><td>{{#crossLink "Graphics/closePath"}}{{/crossLink}} </td>
	 *     <td>c</td><td>{{#crossLink "Graphics/clear"}}{{/crossLink}} </td></tr>
	 *     <tr><td>f</td><td>{{#crossLink "Graphics/beginFill"}}{{/crossLink}} </td>
	 *     <td>lf</td><td>{{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} </td></tr>
	 *     <tr><td>rf</td><td>{{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} </td>
	 *     <td>bf</td><td>{{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} </td></tr>
	 *     <tr><td>ef</td><td>{{#crossLink "Graphics/endFill"}}{{/crossLink}} </td>
	 *     <td>ss / sd</td><td>{{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} / {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} </td></tr>
	 *     <tr><td>s</td><td>{{#crossLink "Graphics/beginStroke"}}{{/crossLink}} </td>
	 *     <td>ls</td><td>{{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} </td></tr>
	 *     <tr><td>rs</td><td>{{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} </td>
	 *     <td>bs</td><td>{{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} </td></tr>
	 *     <tr><td>es</td><td>{{#crossLink "Graphics/endStroke"}}{{/crossLink}} </td>
	 *     <td>dr</td><td>{{#crossLink "Graphics/drawRect"}}{{/crossLink}} </td></tr>
	 *     <tr><td>rr</td><td>{{#crossLink "Graphics/drawRoundRect"}}{{/crossLink}} </td>
	 *     <td>rc</td><td>{{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} </td></tr>
	 *     <tr><td>dc</td><td>{{#crossLink "Graphics/drawCircle"}}{{/crossLink}} </td>
	 *     <td>de</td><td>{{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} </td></tr>
	 *     <tr><td>dp</td><td>{{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} </td>
	 *     <td>p</td><td>{{#crossLink "Graphics/decodePath"}}{{/crossLink}} </td></tr>
	 * </table>
	 *
	 * Here is the above example, using the tiny API instead.
	 *
	 *      myGraphics.s("red").f("blue").r(20, 20, 100, 50);
	 *
	 * @class Graphics
	 * @constructor
	 **/
	function Graphics() {


	// public properties
		/**
		 * Holds a reference to the last command that was created or appended. For example, you could retain a reference
		 * to a Fill command in order to dynamically update the color later by using:
		 *
		 * 		var myFill = myGraphics.beginFill("red").command;
		 * 		// update color later:
		 * 		myFill.style = "yellow";
		 *
		 * @property command
		 * @type Object
		 **/
		this.command = null;


	// private properties
		/**
		 * @property _stroke
		 * @protected
		 * @type {Stroke}
		 **/
		this._stroke = null;

		/**
		 * @property _strokeStyle
		 * @protected
		 * @type {StrokeStyle}
		 **/
		this._strokeStyle = null;
		
		/**
		 * @property _oldStrokeStyle
		 * @protected
		 * @type {StrokeStyle}
		 **/
		this._oldStrokeStyle = null;
		
		/**
		 * @property _strokeDash
		 * @protected
		 * @type {StrokeDash}
		 **/
		this._strokeDash = null;
		
		/**
		 * @property _oldStrokeDash
		 * @protected
		 * @type {StrokeDash}
		 **/
		this._oldStrokeDash = null;

		/**
		 * @property _strokeIgnoreScale
		 * @protected
		 * @type Boolean
		 **/
		this._strokeIgnoreScale = false;

		/**
		 * @property _fill
		 * @protected
		 * @type {Fill}
		 **/
		this._fill = null;

		/**
		 * @property _instructions
		 * @protected
		 * @type {Array}
		 **/
		this._instructions = [];

		/**
		 * Indicates the last instruction index that was committed.
		 * @property _commitIndex
		 * @protected
		 * @type {Number}
		 **/
		this._commitIndex = 0;

		/**
		 * Uncommitted instructions.
		 * @property _activeInstructions
		 * @protected
		 * @type {Array}
		 **/
		this._activeInstructions = [];

		/**
		 * This indicates that there have been changes to the activeInstruction list since the last updateInstructions call.
		 * @property _dirty
		 * @protected
		 * @type {Boolean}
		 * @default false
		 **/
		this._dirty = false;

		/**
		 * Index to draw from if a store operation has happened.
		 * @property _storeIndex
		 * @protected
		 * @type {Number}
		 * @default 0
		 **/
		this._storeIndex = 0;

	// setup:
		this.clear();
	}
	var p = Graphics.prototype;
	var G = Graphics; // shortcut

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// static public methods:
	/**
	 * Returns a CSS compatible color string based on the specified RGB numeric color values in the format
	 * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)". For example,
	 *
	 *      createjs.Graphics.getRGB(50, 100, 150, 0.5);
	 *      // Returns "rgba(50,100,150,0.5)"
	 *
	 * It also supports passing a single hex color value as the first param, and an optional alpha value as the second
	 * param. For example,
	 *
	 *      createjs.Graphics.getRGB(0xFF00FF, 0.2);
	 *      // Returns "rgba(255,0,255,0.2)"
	 *
	 * @method getRGB
	 * @static
	 * @param {Number} r The red component for the color, between 0 and 0xFF (255).
	 * @param {Number} g The green component for the color, between 0 and 0xFF (255).
	 * @param {Number} b The blue component for the color, between 0 and 0xFF (255).
	 * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
	 * @return {String} A CSS compatible color string based on the specified RGB numeric color values in the format
	 * "rgba(255,255,255,1.0)", or if alpha is null then in the format "rgb(255,255,255)".
	 **/
	Graphics.getRGB = function(r, g, b, alpha) {
		if (r != null && b == null) {
			alpha = g;
			b = r&0xFF;
			g = r>>8&0xFF;
			r = r>>16&0xFF;
		}
		if (alpha == null) {
			return "rgb("+r+","+g+","+b+")";
		} else {
			return "rgba("+r+","+g+","+b+","+alpha+")";
		}
	};

	/**
	 * Returns a CSS compatible color string based on the specified HSL numeric color values in the format "hsla(360,100,100,1.0)",
	 * or if alpha is null then in the format "hsl(360,100,100)".
	 *
	 *      createjs.Graphics.getHSL(150, 100, 70);
	 *      // Returns "hsl(150,100,70)"
	 *
	 * @method getHSL
	 * @static
	 * @param {Number} hue The hue component for the color, between 0 and 360.
	 * @param {Number} saturation The saturation component for the color, between 0 and 100.
	 * @param {Number} lightness The lightness component for the color, between 0 and 100.
	 * @param {Number} [alpha] The alpha component for the color where 0 is fully transparent and 1 is fully opaque.
	 * @return {String} A CSS compatible color string based on the specified HSL numeric color values in the format
	 * "hsla(360,100,100,1.0)", or if alpha is null then in the format "hsl(360,100,100)".
	 **/
	Graphics.getHSL = function(hue, saturation, lightness, alpha) {
		if (alpha == null) {
			return "hsl("+(hue%360)+","+saturation+"%,"+lightness+"%)";
		} else {
			return "hsla("+(hue%360)+","+saturation+"%,"+lightness+"%,"+alpha+")";
		}
	};


// static properties:
	/**
	 * A reusable instance of {{#crossLink "Graphics/BeginPath"}}{{/crossLink}} to avoid
	 * unnecessary instantiation.
	 * @property beginCmd
	 * @type {Graphics.BeginPath}
	 * @static
	 **/
	 // defined at the bottom of this file.

	/**
	 * Map of Base64 characters to values. Used by {{#crossLink "Graphics/decodePath"}}{{/crossLink}}.
	 * @property BASE_64
	 * @static
	 * @final
	 * @readonly
	 * @type {Object}
	 **/
	Graphics.BASE_64 = {"A":0,"B":1,"C":2,"D":3,"E":4,"F":5,"G":6,"H":7,"I":8,"J":9,"K":10,"L":11,"M":12,"N":13,"O":14,"P":15,"Q":16,"R":17,"S":18,"T":19,"U":20,"V":21,"W":22,"X":23,"Y":24,"Z":25,"a":26,"b":27,"c":28,"d":29,"e":30,"f":31,"g":32,"h":33,"i":34,"j":35,"k":36,"l":37,"m":38,"n":39,"o":40,"p":41,"q":42,"r":43,"s":44,"t":45,"u":46,"v":47,"w":48,"x":49,"y":50,"z":51,"0":52,"1":53,"2":54,"3":55,"4":56,"5":57,"6":58,"7":59,"8":60,"9":61,"+":62,"/":63};

	/**
	 * Maps numeric values for the caps parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
	 * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
	 * "butt", 1 to "round", and 2 to "square".
	 * For example, to set the line caps to "square":
	 *
	 *      myGraphics.ss(16, 2);
	 *
	 * @property STROKE_CAPS_MAP
	 * @static
	 * @final
	 * @readonly
	 * @type {Array}
	 **/
	Graphics.STROKE_CAPS_MAP = ["butt", "round", "square"];

	/**
	 * Maps numeric values for the joints parameter of {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} to
	 * corresponding string values. This is primarily for use with the tiny API. The mappings are as follows: 0 to
	 * "miter", 1 to "round", and 2 to "bevel".
	 * For example, to set the line joints to "bevel":
	 *
	 *      myGraphics.ss(16, 0, 2);
	 *
	 * @property STROKE_JOINTS_MAP
	 * @static
	 * @final
	 * @readonly
	 * @type {Array}
	 **/
	Graphics.STROKE_JOINTS_MAP = ["miter", "round", "bevel"];

	/**
	 * @property _ctx
	 * @static
	 * @protected
	 * @type {CanvasRenderingContext2D}
	 **/
	var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
	if (canvas.getContext) {
		Graphics._ctx = canvas.getContext("2d");
		canvas.width = canvas.height = 1;
	}


// getter / setters:
	/**
	 * Use the {{#crossLink "Graphics/instructions:property"}}{{/crossLink}} property instead.
	 * @method getInstructions
	 * @return {Array}
	 * @deprecated
	 **/
	p.getInstructions = function() {
		this._updateInstructions();
		return this._instructions;
	};

	/**
	 * Returns the graphics instructions array. Each entry is a graphics command object (ex. Graphics.Fill, Graphics.Rect)
	 * Modifying the returned array directly is not recommended, and is likely to result in unexpected behaviour.
	 *
	 * This property is mainly intended for introspection of the instructions (ex. for graphics export).
	 * @property instructions
	 * @type {Array}
	 * @readonly
	 **/
	try {
		Object.defineProperties(p, {
			instructions: { get: p.getInstructions }
		});
	} catch (e) {}


// public methods:
	/**
	 * Returns true if this Graphics instance has no drawing commands.
	 * @method isEmpty
	 * @return {Boolean} Returns true if this Graphics instance has no drawing commands.
	 **/
	p.isEmpty = function() {
		return !(this._instructions.length || this._activeInstructions.length);
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Object} data Optional data that is passed to graphics command exec methods. When called from a Shape instance, the shape passes itself as the data parameter. This can be used by custom graphic commands to insert contextual data.
	 **/
	p.draw = function(ctx, data) {
		this._updateInstructions();
		var instr = this._instructions;
		for (var i=this._storeIndex, l=instr.length; i<l; i++) {
			instr[i].exec(ctx, data);
		}
	};

	/**
	 * Draws only the path described for this Graphics instance, skipping any non-path instructions, including fill and
	 * stroke descriptions. Used for <code>DisplayObject.mask</code> to draw the clipping path, for example.
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method drawAsPath
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 **/
	p.drawAsPath = function(ctx) {
		this._updateInstructions();
		var instr, instrs = this._instructions;
		for (var i=this._storeIndex, l=instrs.length; i<l; i++) {
			// the first command is always a beginPath command.
			if ((instr = instrs[i]).path !== false) { instr.exec(ctx); }
		}
	};


// public methods that map directly to context 2D calls:
	/**
	 * Moves the drawing point to the specified position. A tiny API method "mt" also exists.
	 * @method moveTo
	 * @param {Number} x The x coordinate the drawing point should move to.
	 * @param {Number} y The y coordinate the drawing point should move to.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
	 * @chainable
	 **/
	p.moveTo = function(x, y) {
		return this.append(new G.MoveTo(x,y), true);
	};

	/**
	 * Draws a line from the current drawing point to the specified position, which become the new current drawing
	 * point. Note that you *must* call {{#crossLink "Graphics/moveTo"}}{{/crossLink}} before the first `lineTo()`.
	 * A tiny API method "lt" also exists.
	 *
	 * For detailed information, read the
	 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#complex-shapes-(paths)">
	 * whatwg spec</a>.
	 * @method lineTo
	 * @param {Number} x The x coordinate the drawing point should draw to.
	 * @param {Number} y The y coordinate the drawing point should draw to.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.lineTo = function(x, y) {
		return this.append(new G.LineTo(x,y));
	};

	/**
	 * Draws an arc with the specified control points and radius.  For detailed information, read the
	 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arcto">
	 * whatwg spec</a>. A tiny API method "at" also exists.
	 * @method arcTo
	 * @param {Number} x1
	 * @param {Number} y1
	 * @param {Number} x2
	 * @param {Number} y2
	 * @param {Number} radius
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.arcTo = function(x1, y1, x2, y2, radius) {
		return this.append(new G.ArcTo(x1, y1, x2, y2, radius));
	};

	/**
	 * Draws an arc defined by the radius, startAngle and endAngle arguments, centered at the position (x, y). For
	 * example, to draw a full circle with a radius of 20 centered at (100, 100):
	 *
	 *      arc(100, 100, 20, 0, Math.PI*2);
	 *
	 * For detailed information, read the
	 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-arc">whatwg spec</a>.
	 * A tiny API method "a" also exists.
	 * @method arc
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} radius
	 * @param {Number} startAngle Measured in radians.
	 * @param {Number} endAngle Measured in radians.
	 * @param {Boolean} anticlockwise
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
		return this.append(new G.Arc(x, y, radius, startAngle, endAngle, anticlockwise));
	};

	/**
	 * Draws a quadratic curve from the current drawing point to (x, y) using the control point (cpx, cpy). For detailed
	 * information, read the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-quadraticcurveto">
	 * whatwg spec</a>. A tiny API method "qt" also exists.
	 * @method quadraticCurveTo
	 * @param {Number} cpx
	 * @param {Number} cpy
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.quadraticCurveTo = function(cpx, cpy, x, y) {
		return this.append(new G.QuadraticCurveTo(cpx, cpy, x, y));
	};

	/**
	 * Draws a bezier curve from the current drawing point to (x, y) using the control points (cp1x, cp1y) and (cp2x,
	 * cp2y). For detailed information, read the
	 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-beziercurveto">
	 * whatwg spec</a>. A tiny API method "bt" also exists.
	 * @method bezierCurveTo
	 * @param {Number} cp1x
	 * @param {Number} cp1y
	 * @param {Number} cp2x
	 * @param {Number} cp2y
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
		return this.append(new G.BezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y));
	};

	/**
	 * Draws a rectangle at (x, y) with the specified width and height using the current fill and/or stroke.
	 * For detailed information, read the
	 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-rect">
	 * whatwg spec</a>. A tiny API method "r" also exists.
	 * @method rect
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w Width of the rectangle
	 * @param {Number} h Height of the rectangle
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.rect = function(x, y, w, h) {
		return this.append(new G.Rect(x, y, w, h));
	};

	/**
	 * Closes the current path, effectively drawing a line from the current drawing point to the first drawing point specified
	 * since the fill or stroke was last set. A tiny API method "cp" also exists.
	 * @method closePath
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.closePath = function() {
		return this._activeInstructions.length ? this.append(new G.ClosePath()) : this;
	};


// public methods that roughly map to Flash graphics APIs:
	/**
	 * Clears all drawing instructions, effectively resetting this Graphics instance. Any line and fill styles will need
	 * to be redefined to draw shapes following a clear call. A tiny API method "c" also exists.
	 * @method clear
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.clear = function() {
		this._instructions.length = this._activeInstructions.length = this._commitIndex = 0;
		this._strokeStyle = this._oldStrokeStyle = this._stroke = this._fill = this._strokeDash = this._oldStrokeDash = null;
		this._dirty = this._strokeIgnoreScale = false;
		return this;
	};

	/**
	 * Begins a fill with the specified color. This ends the current sub-path. A tiny API method "f" also exists.
	 * @method beginFill
	 * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
	 * null will result in no fill.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginFill = function(color) {
		return this._setFill(color ? new G.Fill(color) : null);
	};

	/**
	 * Begins a linear gradient fill defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
	 * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
	 * square to display it:
	 *
	 *      myGraphics.beginLinearGradientFill(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
	 *
	 * A tiny API method "lf" also exists.
	 * @method beginLinearGradientFill
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
	 * drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
	 * the first color to 10% then interpolating to the second color at 90%.
	 * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
	 * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginLinearGradientFill = function(colors, ratios, x0, y0, x1, y1) {
		return this._setFill(new G.Fill().linearGradient(colors, ratios, x0, y0, x1, y1));
	};

	/**
	 * Begins a radial gradient fill. This ends the current sub-path. For example, the following code defines a red to
	 * blue radial gradient centered at (100, 100), with a radius of 50, and draws a circle to display it:
	 *
	 *      myGraphics.beginRadialGradientFill(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50).drawCircle(100, 100, 50);
	 *
	 * A tiny API method "rf" also exists.
	 * @method beginRadialGradientFill
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
	 * a gradient drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
	 * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
	 * @param {Number} x0 Center position of the inner circle that defines the gradient.
	 * @param {Number} y0 Center position of the inner circle that defines the gradient.
	 * @param {Number} r0 Radius of the inner circle that defines the gradient.
	 * @param {Number} x1 Center position of the outer circle that defines the gradient.
	 * @param {Number} y1 Center position of the outer circle that defines the gradient.
	 * @param {Number} r1 Radius of the outer circle that defines the gradient.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginRadialGradientFill = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
		return this._setFill(new G.Fill().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
	};

	/**
	 * Begins a pattern fill using the specified image. This ends the current sub-path. A tiny API method "bf" also
	 * exists.
	 * @method beginBitmapFill
	 * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
	 * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
	 * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
	 * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
	 * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
	 * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
	 * will be applied relative to the parent transform.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginBitmapFill = function(image, repetition, matrix) {
		return this._setFill(new G.Fill(null,matrix).bitmap(image, repetition));
	};

	/**
	 * Ends the current sub-path, and begins a new one with no fill. Functionally identical to <code>beginFill(null)</code>.
	 * A tiny API method "ef" also exists.
	 * @method endFill
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.endFill = function() {
		return this.beginFill();
	};

	/**
	 * Sets the stroke style. Like all drawing methods, this can be chained, so you can define
	 * the stroke style and color in a single line of code like so:
	 *
	 * 	myGraphics.setStrokeStyle(8,"round").beginStroke("#F00");
	 *
	 * A tiny API method "ss" also exists.
	 * @method setStrokeStyle
	 * @param {Number} thickness The width of the stroke.
	 * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
	 * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
	 * the tiny API.
	 * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
	 * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
	 * for use with the tiny API.
	 * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
	 * controls at what point a mitered joint will be clipped.
	 * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
	 * of active transformations.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.setStrokeStyle = function(thickness, caps, joints, miterLimit, ignoreScale) {
		this._updateInstructions(true);
		this._strokeStyle = this.command = new G.StrokeStyle(thickness, caps, joints, miterLimit, ignoreScale);

		// ignoreScale lives on Stroke, not StrokeStyle, so we do a little trickery:
		if (this._stroke) { this._stroke.ignoreScale = ignoreScale; }
		this._strokeIgnoreScale = ignoreScale;
		return this;
	};
	
	/**
	 * Sets or clears the stroke dash pattern.
	 *
	 * 	myGraphics.setStrokeDash([20, 10], 0);
	 *
	 * A tiny API method `sd` also exists.
	 * @method setStrokeDash
	 * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
	 * For example, `[20,10]` would create a pattern of 20 pixel lines with 10 pixel gaps between them.
	 * Passing null or an empty array will clear the existing stroke dash.
	 * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.setStrokeDash = function(segments, offset) {
		this._updateInstructions(true);
		this._strokeDash = this.command = new G.StrokeDash(segments, offset);
		return this;
	};

	/**
	 * Begins a stroke with the specified color. This ends the current sub-path. A tiny API method "s" also exists.
	 * @method beginStroke
	 * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
	 * null will result in no stroke.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginStroke = function(color) {
		return this._setStroke(color ? new G.Stroke(color) : null);
	};

	/**
	 * Begins a linear gradient stroke defined by the line (x0, y0) to (x1, y1). This ends the current sub-path. For
	 * example, the following code defines a black to white vertical gradient ranging from 20px to 120px, and draws a
	 * square to display it:
	 *
	 *      myGraphics.setStrokeStyle(10).
	 *          beginLinearGradientStroke(["#000","#FFF"], [0, 1], 0, 20, 0, 120).drawRect(20, 20, 120, 120);
	 *
	 * A tiny API method "ls" also exists.
	 * @method beginLinearGradientStroke
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
	 * a gradient drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
	 * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
	 * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
	 * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginLinearGradientStroke = function(colors, ratios, x0, y0, x1, y1) {
		return this._setStroke(new G.Stroke().linearGradient(colors, ratios, x0, y0, x1, y1));
	};

	/**
	 * Begins a radial gradient stroke. This ends the current sub-path. For example, the following code defines a red to
	 * blue radial gradient centered at (100, 100), with a radius of 50, and draws a rectangle to display it:
	 *
	 *      myGraphics.setStrokeStyle(10)
	 *          .beginRadialGradientStroke(["#F00","#00F"], [0, 1], 100, 100, 0, 100, 100, 50)
	 *          .drawRect(50, 90, 150, 110);
	 *
	 * A tiny API method "rs" also exists.
	 * @method beginRadialGradientStroke
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
	 * a gradient drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
	 * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
	 * to 100%.
	 * @param {Number} x0 Center position of the inner circle that defines the gradient.
	 * @param {Number} y0 Center position of the inner circle that defines the gradient.
	 * @param {Number} r0 Radius of the inner circle that defines the gradient.
	 * @param {Number} x1 Center position of the outer circle that defines the gradient.
	 * @param {Number} y1 Center position of the outer circle that defines the gradient.
	 * @param {Number} r1 Radius of the outer circle that defines the gradient.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginRadialGradientStroke = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
		return this._setStroke(new G.Stroke().radialGradient(colors, ratios, x0, y0, r0, x1, y1, r1));
	};

	/**
	 * Begins a pattern fill using the specified image. This ends the current sub-path. Note that unlike bitmap fills,
	 * strokes do not currently support a matrix parameter due to limitations in the canvas API. A tiny API method "bs"
	 * also exists.
	 * @method beginBitmapStroke
	 * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
	 * as the pattern. Must be loaded prior to creating a bitmap fill, or the fill will be empty.
	 * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
	 * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.beginBitmapStroke = function(image, repetition) {
		// NOTE: matrix is not supported for stroke because transforms on strokes also affect the drawn stroke width.
		return this._setStroke(new G.Stroke().bitmap(image, repetition));
	};

	/**
	 * Ends the current sub-path, and begins a new one with no stroke. Functionally identical to <code>beginStroke(null)</code>.
	 * A tiny API method "es" also exists.
	 * @method endStroke
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.endStroke = function() {
		return this.beginStroke();
	};

	/**
	 * Maps the familiar ActionScript <code>curveTo()</code> method to the functionally similar {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}}
	 * method.
	 * @method quadraticCurveTo
	 * @param {Number} cpx
	 * @param {Number} cpy
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.curveTo = p.quadraticCurveTo;

	/**
	 *
	 * Maps the familiar ActionScript <code>drawRect()</code> method to the functionally similar {{#crossLink "Graphics/rect"}}{{/crossLink}}
	 * method.
	 * @method drawRect
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w Width of the rectangle
	 * @param {Number} h Height of the rectangle
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.drawRect = p.rect;

	/**
	 * Draws a rounded rectangle with all corners with the specified radius.
	 * @method drawRoundRect
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w
	 * @param {Number} h
	 * @param {Number} radius Corner radius.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.drawRoundRect = function(x, y, w, h, radius) {
		return this.drawRoundRectComplex(x, y, w, h, radius, radius, radius, radius);
	};

	/**
	 * Draws a rounded rectangle with different corner radii. Supports positive and negative corner radii. A tiny API
	 * method "rc" also exists.
	 * @method drawRoundRectComplex
	 * @param {Number} x The horizontal coordinate to draw the round rect.
	 * @param {Number} y The vertical coordinate to draw the round rect.
	 * @param {Number} w The width of the round rect.
	 * @param {Number} h The height of the round rect.
	 * @param {Number} radiusTL Top left corner radius.
	 * @param {Number} radiusTR Top right corner radius.
	 * @param {Number} radiusBR Bottom right corner radius.
	 * @param {Number} radiusBL Bottom left corner radius.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.drawRoundRectComplex = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
		return this.append(new G.RoundRect(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL));
	};

	/**
	 * Draws a circle with the specified radius at (x, y).
	 *
	 *      var g = new createjs.Graphics();
	 *	    g.setStrokeStyle(1);
	 *	    g.beginStroke(createjs.Graphics.getRGB(0,0,0));
	 *	    g.beginFill(createjs.Graphics.getRGB(255,0,0));
	 *	    g.drawCircle(0,0,3);
	 *
	 *	    var s = new createjs.Shape(g);
	 *		s.x = 100;
	 *		s.y = 100;
	 *
	 *	    stage.addChild(s);
	 *	    stage.update();
	 *
	 * A tiny API method "dc" also exists.
	 * @method drawCircle
	 * @param {Number} x x coordinate center point of circle.
	 * @param {Number} y y coordinate center point of circle.
	 * @param {Number} radius Radius of circle.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.drawCircle = function(x, y, radius) {
		return this.append(new G.Circle(x, y, radius));
	};

	/**
	 * Draws an ellipse (oval) with a specified width (w) and height (h). Similar to {{#crossLink "Graphics/drawCircle"}}{{/crossLink}},
	 * except the width and height can be different. A tiny API method "de" also exists.
	 * @method drawEllipse
	 * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
	 * which draws from center.
	 * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
	 * which draws from the center.
	 * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
	 * number.
	 * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.drawEllipse = function(x, y, w, h) {
		return this.append(new G.Ellipse(x, y, w, h));
	};

	/**
	 * Draws a star if pointSize is greater than 0, or a regular polygon if pointSize is 0 with the specified number of
	 * points. For example, the following code will draw a familiar 5 pointed star shape centered at 100, 100 and with a
	 * radius of 50:
	 *
	 *      myGraphics.beginFill("#FF0").drawPolyStar(100, 100, 50, 5, 0.6, -90);
	 *      // Note: -90 makes the first point vertical
	 *
	 * A tiny API method "dp" also exists.
	 *
	 * @method drawPolyStar
	 * @param {Number} x Position of the center of the shape.
	 * @param {Number} y Position of the center of the shape.
	 * @param {Number} radius The outer radius of the shape.
	 * @param {Number} sides The number of points on the star or sides on the polygon.
	 * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
	 * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
	 * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
	 * directly to the right of the center.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.drawPolyStar = function(x, y, radius, sides, pointSize, angle) {
		return this.append(new G.PolyStar(x, y, radius, sides, pointSize, angle));
	};

	// TODO: deprecated.
	/**
	 * Removed in favour of using custom command objects with {{#crossLink "Graphics/append"}}{{/crossLink}}.
	 * @method inject
	 * @deprecated
	 **/

	/**
	 * Appends a graphics command object to the graphics queue. Command objects expose an "exec" method
	 * that accepts two parameters: the Context2D to operate on, and an arbitrary data object passed into
	 * {{#crossLink "Graphics/draw"}}{{/crossLink}}. The latter will usually be the Shape instance that called draw.
	 *
	 * This method is used internally by Graphics methods, such as drawCircle, but can also be used directly to insert
	 * built-in or custom graphics commands. For example:
	 *
	 * 		// attach data to our shape, so we can access it during the draw:
	 * 		myShape.color = "red";
	 *
	 * 		// append a Circle command object:
	 * 		myShape.graphics.append(new createjs.Graphics.Circle(50, 50, 30));
	 *
	 * 		// append a custom command object with an exec method that sets the fill style
	 * 		// based on the shape's data, and then fills the circle.
	 * 		myShape.graphics.append({exec:function(ctx, shape) {
	 * 			ctx.fillStyle = shape.color;
	 * 			ctx.fill();
	 * 		}});
	 *
	 * @method append
	 * @param {Object} command A graphics command object exposing an "exec" method.
	 * @param {boolean} clean The clean param is primarily for internal use. A value of true indicates that a command does not generate a path that should be stroked or filled.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.append = function(command, clean) {
		this._activeInstructions.push(command);
		this.command = command;
		if (!clean) { this._dirty = true; }
		return this;
	};

	/**
	 * Decodes a compact encoded path string into a series of draw instructions.
	 * This format is not intended to be human readable, and is meant for use by authoring tools.
	 * The format uses a base64 character set, with each character representing 6 bits, to define a series of draw
	 * commands.
	 *
	 * Each command is comprised of a single "header" character followed by a variable number of alternating x and y
	 * position values. Reading the header bits from left to right (most to least significant): bits 1 to 3 specify the
	 * type of operation (0-moveTo, 1-lineTo, 2-quadraticCurveTo, 3-bezierCurveTo, 4-closePath, 5-7 unused). Bit 4
	 * indicates whether position values use 12 bits (2 characters) or 18 bits (3 characters), with a one indicating the
	 * latter. Bits 5 and 6 are currently unused.
	 *
	 * Following the header is a series of 0 (closePath), 2 (moveTo, lineTo), 4 (quadraticCurveTo), or 6 (bezierCurveTo)
	 * parameters. These parameters are alternating x/y positions represented by 2 or 3 characters (as indicated by the
	 * 4th bit in the command char). These characters consist of a 1 bit sign (1 is negative, 0 is positive), followed
	 * by an 11 (2 char) or 17 (3 char) bit integer value. All position values are in tenths of a pixel. Except in the
	 * case of move operations which are absolute, this value is a delta from the previous x or y position (as
	 * appropriate).
	 *
	 * For example, the string "A3cAAMAu4AAA" represents a line starting at -150,0 and ending at 150,0.
	 * <br />A - bits 000000. First 3 bits (000) indicate a moveTo operation. 4th bit (0) indicates 2 chars per
	 * parameter.
	 * <br />n0 - 110111011100. Absolute x position of -150.0px. First bit indicates a negative value, remaining bits
	 * indicate 1500 tenths of a pixel.
	 * <br />AA - 000000000000. Absolute y position of 0.
	 * <br />I - 001100. First 3 bits (001) indicate a lineTo operation. 4th bit (1) indicates 3 chars per parameter.
	 * <br />Au4 - 000000101110111000. An x delta of 300.0px, which is added to the previous x value of -150.0px to
	 * provide an absolute position of +150.0px.
	 * <br />AAA - 000000000000000000. A y delta value of 0.
	 *
	 * A tiny API method "p" also exists.
	 * @method decodePath
	 * @param {String} str The path string to decode.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.decodePath = function(str) {
		var instructions = [this.moveTo, this.lineTo, this.quadraticCurveTo, this.bezierCurveTo, this.closePath];
		var paramCount = [2, 2, 4, 6, 0];
		var i=0, l=str.length;
		var params = [];
		var x=0, y=0;
		var base64 = Graphics.BASE_64;

		while (i<l) {
			var c = str.charAt(i);
			var n = base64[c];
			var fi = n>>3; // highest order bits 1-3 code for operation.
			var f = instructions[fi];
			// check that we have a valid instruction & that the unused bits are empty:
			if (!f || (n&3)) { throw("bad path data (@"+i+"): "+c); }
			var pl = paramCount[fi];
			if (!fi) { x=y=0; } // move operations reset the position.
			params.length = 0;
			i++;
			var charCount = (n>>2&1)+2;  // 4th header bit indicates number size for this operation.
			for (var p=0; p<pl; p++) {
				var num = base64[str.charAt(i)];
				var sign = (num>>5) ? -1 : 1;
				num = ((num&31)<<6)|(base64[str.charAt(i+1)]);
				if (charCount == 3) { num = (num<<6)|(base64[str.charAt(i+2)]); }
				num = sign*num/10;
				if (p%2) { x = (num += x); }
				else { y = (num += y); }
				params[p] = num;
				i += charCount;
			}
			f.apply(this,params);
		}
		return this;
	};

	/**
	 * Stores all graphics commands so they won't be executed in future draws. Calling store() a second time adds to
	 * the existing store. This also affects `drawAsPath()`.
	 *
	 * This is useful in cases where you are creating vector graphics in an iterative manner (ex. generative art), so
	 * that only new graphics need to be drawn (which can provide huge performance benefits), but you wish to retain all
	 * of the vector instructions for later use (ex. scaling, modifying, or exporting).
	 *
	 * Note that calling store() will force the active path (if any) to be ended in a manner similar to changing
	 * the fill or stroke.
	 *
	 * For example, consider a application where the user draws lines with the mouse. As each line segment (or collection of
	 * segments) are added to a Shape, it can be rasterized using {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}},
	 * and then stored, so that it can be redrawn at a different scale when the application is resized, or exported to SVG.
	 *
	 * 	// set up cache:
	 * 	myShape.cache(0,0,500,500,scale);
	 *
	 * 	// when the user drags, draw a new line:
	 * 	myShape.graphics.moveTo(oldX,oldY).lineTo(newX,newY);
	 * 	// then draw it into the existing cache:
	 * 	myShape.updateCache("source-over");
	 * 	// store the new line, so it isn't redrawn next time:
	 * 	myShape.store();
	 *
	 * 	// then, when the window resizes, we can re-render at a different scale:
	 * 	// first, unstore all our lines:
	 * 	myShape.unstore();
	 * 	// then cache using the new scale:
	 * 	myShape.cache(0,0,500,500,newScale);
	 * 	// finally, store the existing commands again:
	 * 	myShape.store();
	 *
	 * @method store
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.store = function() {
		this._updateInstructions(true);
		this._storeIndex = this._instructions.length;
		return this;
	};

	/**
	 * Unstores any graphics commands that were previously stored using {{#crossLink "Graphics/store"}}{{/crossLink}}
	 * so that they will be executed in subsequent draw calls.
	 *
	 * @method unstore
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.unstore = function() {
		this._storeIndex = 0;
		return this;
	};

	/**
	 * Returns a clone of this Graphics instance. Note that the individual command objects are not cloned.
	 * @method clone
	 * @return {Graphics} A clone of the current Graphics instance.
	 **/
	p.clone = function() {
		var o = new Graphics();
		o.command = this.command;
		o._stroke = this._stroke;
		o._strokeStyle = this._strokeStyle;
		o._strokeDash = this._strokeDash;
		o._strokeIgnoreScale = this._strokeIgnoreScale;
		o._fill = this._fill;
		o._instructions = this._instructions.slice();
		o._commitIndex = this._commitIndex;
		o._activeInstructions = this._activeInstructions.slice();
		o._dirty = this._dirty;
		o._storeIndex = this._storeIndex;
		return o;
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Graphics]";
	};


// tiny API:
	/**
	 * Shortcut to moveTo.
	 * @method mt
	 * @param {Number} x The x coordinate the drawing point should move to.
	 * @param {Number} y The y coordinate the drawing point should move to.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls).
	 * @chainable
	 * @protected
	 **/
	p.mt = p.moveTo;

	/**
	 * Shortcut to lineTo.
	 * @method lt
	 * @param {Number} x The x coordinate the drawing point should draw to.
	 * @param {Number} y The y coordinate the drawing point should draw to.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.lt = p.lineTo;

	/**
	 * Shortcut to arcTo.
	 * @method at
	 * @param {Number} x1
	 * @param {Number} y1
	 * @param {Number} x2
	 * @param {Number} y2
	 * @param {Number} radius
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.at = p.arcTo;

	/**
	 * Shortcut to bezierCurveTo.
	 * @method bt
	 * @param {Number} cp1x
	 * @param {Number} cp1y
	 * @param {Number} cp2x
	 * @param {Number} cp2y
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.bt = p.bezierCurveTo;

	/**
	 * Shortcut to quadraticCurveTo / curveTo.
	 * @method qt
	 * @param {Number} cpx
	 * @param {Number} cpy
	 * @param {Number} x
	 * @param {Number} y
	 * @protected
	 * @chainable
	 **/
	p.qt = p.quadraticCurveTo;

	/**
	 * Shortcut to arc.
	 * @method a
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} radius
	 * @param {Number} startAngle Measured in radians.
	 * @param {Number} endAngle Measured in radians.
	 * @param {Boolean} anticlockwise
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @protected
	 * @chainable
	 **/
	p.a = p.arc;

	/**
	 * Shortcut to rect.
	 * @method r
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w Width of the rectangle
	 * @param {Number} h Height of the rectangle
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.r = p.rect;

	/**
	 * Shortcut to closePath.
	 * @method cp
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.cp = p.closePath;

	/**
	 * Shortcut to clear.
	 * @method c
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.c = p.clear;

	/**
	 * Shortcut to beginFill.
	 * @method f
	 * @param {String} color A CSS compatible color value (ex. "red", "#FF0000", or "rgba(255,0,0,0.5)"). Setting to
	 * null will result in no fill.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.f = p.beginFill;

	/**
	 * Shortcut to beginLinearGradientFill.
	 * @method lf
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define a gradient
	 * drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1, 0.9] would draw
	 * the first color to 10% then interpolating to the second color at 90%.
	 * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
	 * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.lf = p.beginLinearGradientFill;

	/**
	 * Shortcut to beginRadialGradientFill.
	 * @method rf
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
	 * a gradient drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
	 * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
	 * @param {Number} x0 Center position of the inner circle that defines the gradient.
	 * @param {Number} y0 Center position of the inner circle that defines the gradient.
	 * @param {Number} r0 Radius of the inner circle that defines the gradient.
	 * @param {Number} x1 Center position of the outer circle that defines the gradient.
	 * @param {Number} y1 Center position of the outer circle that defines the gradient.
	 * @param {Number} r1 Radius of the outer circle that defines the gradient.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.rf = p.beginRadialGradientFill;

	/**
	 * Shortcut to beginBitmapFill.
	 * @method bf
	 * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
	 * as the pattern.
	 * @param {String} repetition Optional. Indicates whether to repeat the image in the fill area. One of "repeat",
	 * "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat". Note that Firefox does not support "repeat-x" or
	 * "repeat-y" (latest tests were in FF 20.0), and will default to "repeat".
	 * @param {Matrix2D} matrix Optional. Specifies a transformation matrix for the bitmap fill. This transformation
	 * will be applied relative to the parent transform.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.bf = p.beginBitmapFill;

	/**
	 * Shortcut to endFill.
	 * @method ef
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.ef = p.endFill;

	/**
	 * Shortcut to setStrokeStyle.
	 * @method ss
	 * @param {Number} thickness The width of the stroke.
	 * @param {String | Number} [caps=0] Indicates the type of caps to use at the end of lines. One of butt,
	 * round, or square. Defaults to "butt". Also accepts the values 0 (butt), 1 (round), and 2 (square) for use with
	 * the tiny API.
	 * @param {String | Number} [joints=0] Specifies the type of joints that should be used where two lines meet.
	 * One of bevel, round, or miter. Defaults to "miter". Also accepts the values 0 (miter), 1 (round), and 2 (bevel)
	 * for use with the tiny API.
	 * @param {Number} [miterLimit=10] If joints is set to "miter", then you can specify a miter limit ratio which
	 * controls at what point a mitered joint will be clipped.
	 * @param {Boolean} [ignoreScale=false] If true, the stroke will be drawn at the specified thickness regardless
	 * of active transformations.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.ss = p.setStrokeStyle;
	
	/**
	 * Shortcut to setStrokeDash.
	 * @method sd
	 * @param {Array} [segments] An array specifying the dash pattern, alternating between line and gap.
	 * For example, [20,10] would create a pattern of 20 pixel lines with 10 pixel gaps between them.
	 * Passing null or an empty array will clear any existing dash.
	 * @param {Number} [offset=0] The offset of the dash pattern. For example, you could increment this value to create a "marching ants" effect.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.sd = p.setStrokeDash;

	/**
	 * Shortcut to beginStroke.
	 * @method s
	 * @param {String} color A CSS compatible color value (ex. "#FF0000", "red", or "rgba(255,0,0,0.5)"). Setting to
	 * null will result in no stroke.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.s = p.beginStroke;

	/**
	 * Shortcut to beginLinearGradientStroke.
	 * @method ls
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
	 * a gradient drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
	 * 0.9] would draw the first color to 10% then interpolating to the second color at 90%.
	 * @param {Number} x0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} y0 The position of the first point defining the line that defines the gradient direction and size.
	 * @param {Number} x1 The position of the second point defining the line that defines the gradient direction and size.
	 * @param {Number} y1 The position of the second point defining the line that defines the gradient direction and size.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.ls = p.beginLinearGradientStroke;

	/**
	 * Shortcut to beginRadialGradientStroke.
	 * @method rs
	 * @param {Array} colors An array of CSS compatible color values. For example, ["#F00","#00F"] would define
	 * a gradient drawing from red to blue.
	 * @param {Array} ratios An array of gradient positions which correspond to the colors. For example, [0.1,
	 * 0.9] would draw the first color to 10% then interpolating to the second color at 90%, then draw the second color
	 * to 100%.
	 * @param {Number} x0 Center position of the inner circle that defines the gradient.
	 * @param {Number} y0 Center position of the inner circle that defines the gradient.
	 * @param {Number} r0 Radius of the inner circle that defines the gradient.
	 * @param {Number} x1 Center position of the outer circle that defines the gradient.
	 * @param {Number} y1 Center position of the outer circle that defines the gradient.
	 * @param {Number} r1 Radius of the outer circle that defines the gradient.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.rs = p.beginRadialGradientStroke;

	/**
	 * Shortcut to beginBitmapStroke.
	 * @method bs
	 * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image The Image, Canvas, or Video object to use
	 * as the pattern.
	 * @param {String} [repetition=repeat] Optional. Indicates whether to repeat the image in the fill area. One of
	 * "repeat", "repeat-x", "repeat-y", or "no-repeat". Defaults to "repeat".
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.bs = p.beginBitmapStroke;

	/**
	 * Shortcut to endStroke.
	 * @method es
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.es = p.endStroke;

	/**
	 * Shortcut to drawRect.
	 * @method dr
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w Width of the rectangle
	 * @param {Number} h Height of the rectangle
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.dr = p.drawRect;

	/**
	 * Shortcut to drawRoundRect.
	 * @method rr
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w
	 * @param {Number} h
	 * @param {Number} radius Corner radius.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.rr = p.drawRoundRect;

	/**
	 * Shortcut to drawRoundRectComplex.
	 * @method rc
	 * @param {Number} x The horizontal coordinate to draw the round rect.
	 * @param {Number} y The vertical coordinate to draw the round rect.
	 * @param {Number} w The width of the round rect.
	 * @param {Number} h The height of the round rect.
	 * @param {Number} radiusTL Top left corner radius.
	 * @param {Number} radiusTR Top right corner radius.
	 * @param {Number} radiusBR Bottom right corner radius.
	 * @param {Number} radiusBL Bottom left corner radius.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.rc = p.drawRoundRectComplex;

	/**
	 * Shortcut to drawCircle.
	 * @method dc
	 * @param {Number} x x coordinate center point of circle.
	 * @param {Number} y y coordinate center point of circle.
	 * @param {Number} radius Radius of circle.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.dc = p.drawCircle;

	/**
	 * Shortcut to drawEllipse.
	 * @method de
	 * @param {Number} x The left coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
	 * which draws from center.
	 * @param {Number} y The top coordinate point of the ellipse. Note that this is different from {{#crossLink "Graphics/drawCircle"}}{{/crossLink}}
	 * which draws from the center.
	 * @param {Number} w The height (horizontal diameter) of the ellipse. The horizontal radius will be half of this
	 * number.
	 * @param {Number} h The width (vertical diameter) of the ellipse. The vertical radius will be half of this number.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.de = p.drawEllipse;

	/**
	 * Shortcut to drawPolyStar.
	 * @method dp
	 * @param {Number} x Position of the center of the shape.
	 * @param {Number} y Position of the center of the shape.
	 * @param {Number} radius The outer radius of the shape.
	 * @param {Number} sides The number of points on the star or sides on the polygon.
	 * @param {Number} pointSize The depth or "pointy-ness" of the star points. A pointSize of 0 will draw a regular
	 * polygon (no points), a pointSize of 1 will draw nothing because the points are infinitely pointy.
	 * @param {Number} angle The angle of the first point / corner. For example a value of 0 will draw the first point
	 * directly to the right of the center.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.dp = p.drawPolyStar;

	/**
	 * Shortcut to decodePath.
	 * @method p
	 * @param {String} str The path string to decode.
	 * @return {Graphics} The Graphics instance the method is called on (useful for chaining calls.)
	 * @chainable
	 * @protected
	 **/
	p.p = p.decodePath;


// private methods:
	/**
	 * @method _updateInstructions
	 * @param commit
	 * @protected
	 **/
	p._updateInstructions = function(commit) {
		var instr = this._instructions, active = this._activeInstructions, commitIndex = this._commitIndex;

		if (this._dirty && active.length) {
			instr.length = commitIndex; // remove old, uncommitted commands
			instr.push(Graphics.beginCmd);

			var l = active.length, ll = instr.length;
			instr.length = ll+l;
			for (var i=0; i<l; i++) { instr[i+ll] = active[i]; }

			if (this._fill) { instr.push(this._fill); }
			if (this._stroke) {
				// doesn't need to be re-applied if it hasn't changed.
				if (this._strokeDash !== this._oldStrokeDash) {
					this._oldStrokeDash = this._strokeDash;
					instr.push(this._strokeDash);
				}
				if (this._strokeStyle !== this._oldStrokeStyle) {
					this._oldStrokeStyle = this._strokeStyle;
					instr.push(this._strokeStyle);
				}
				instr.push(this._stroke);
			}

			this._dirty = false;
		}

		if (commit) {
			active.length = 0;
			this._commitIndex = instr.length;
		}
	};

	/**
	 * @method _setFill
	 * @param fill
	 * @protected
	 **/
	p._setFill = function(fill) {
		this._updateInstructions(true);
		this.command = this._fill = fill;
		return this;
	};

	/**
	 * @method _setStroke
	 * @param stroke
	 * @protected
	 **/
	p._setStroke = function(stroke) {
		this._updateInstructions(true);
		if (this.command = this._stroke = stroke) {
			stroke.ignoreScale = this._strokeIgnoreScale;
		}
		return this;
	};

// Command Objects:
	/**
	 * @namespace Graphics
	 */
	/**
	 * Graphics command object. See {{#crossLink "Graphics/lineTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class LineTo
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.LineTo = function(x, y) {
		this.x = x; this.y = y;
	}).prototype.exec = function(ctx) { ctx.lineTo(this.x,this.y); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/moveTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class MoveTo
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx
	 */
	(G.MoveTo = function(x, y) {
		this.x = x; this.y = y;
	}).prototype.exec = function(ctx) { ctx.moveTo(this.x, this.y); };


	/**
	 * Graphics command object. See {{#crossLink "Graphics/arcTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class ArcTo
	 * @constructor
	 * @param {Number} x1
	 * @param {Number} y1
	 * @param {Number} x2
	 * @param {Number} y2
	 * @param {Number} radius
	 **/
	/**
	 * @property x1
	 * @type Number
	 */
	/**
	 * @property y1
	 * @type Number
	 */
	/**
	 * @property x2
	 * @type Number
	 */
	/**
	 * @property y2
	 * @type Number
	 */
	/**
	 * @property radius
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.ArcTo = function(x1, y1, x2, y2, radius) {
		this.x1 = x1; this.y1 = y1;
		this.x2 = x2; this.y2 = y2;
		this.radius = radius;
	}).prototype.exec = function(ctx) { ctx.arcTo(this.x1, this.y1, this.x2, this.y2, this.radius); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/arc"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class Arc
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} radius
	 * @param {Number} startAngle
	 * @param {Number} endAngle
	 * @param {Number} anticlockwise
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @property radius
	 * @type Number
	 */
	/**
	 * @property startAngle
	 * @type Number
	 */
	/**
	 * @property endAngle
	 * @type Number
	 */
	/**
	 * @property anticlockwise
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.Arc = function(x, y, radius, startAngle, endAngle, anticlockwise) {
		this.x = x; this.y = y;
		this.radius = radius;
		this.startAngle = startAngle; this.endAngle = endAngle;
		this.anticlockwise = !!anticlockwise;
	}).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, this.startAngle, this.endAngle, this.anticlockwise); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/quadraticCurveTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class QuadraticCurveTo
	 * @constructor
	 * @param {Number} cpx
	 * @param {Number} cpy
	 * @param {Number} x
	 * @param {Number} y
	 **/
	/**
	 * @property cpx
	 * @type Number
	 */
	/**
	 * @property cpy
	 * @type Number
	 */
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.QuadraticCurveTo = function(cpx, cpy, x, y) {
		this.cpx = cpx; this.cpy = cpy;
		this.x = x; this.y = y;
	}).prototype.exec = function(ctx) { ctx.quadraticCurveTo(this.cpx, this.cpy, this.x, this.y); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/bezierCurveTo"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class BezierCurveTo
	 * @constructor
	 * @param {Number} cp1x
	 * @param {Number} cp1y
	 * @param {Number} cp2x
	 * @param {Number} cp2y
	 * @param {Number} x
	 * @param {Number} y
	 **/
	/**
	 * @property cp1x
	 * @type Number
	 */
	/**
	 * @property cp1y
	 * @type Number
	 */
	/**
	 * @property cp2x
	 * @type Number
	 */
	/**
	 * @property cp2y
	 * @type Number
	 */
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.BezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
		this.cp1x = cp1x; this.cp1y = cp1y;
		this.cp2x = cp2x; this.cp2y = cp2y;
		this.x = x; this.y = y;
	}).prototype.exec = function(ctx) { ctx.bezierCurveTo(this.cp1x, this.cp1y, this.cp2x, this.cp2y, this.x, this.y); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/rect"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class Rect
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w
	 * @param {Number} h
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @property w
	 * @type Number
	 */
	/**
	 * @property h
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.Rect = function(x, y, w, h) {
		this.x = x; this.y = y;
		this.w = w; this.h = h;
	}).prototype.exec = function(ctx) { ctx.rect(this.x, this.y, this.w, this.h); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/closePath"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class ClosePath
	 * @constructor
	 **/
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.ClosePath = function() {
	}).prototype.exec = function(ctx) { ctx.closePath(); };

	/**
	 * Graphics command object to begin a new path. See {{#crossLink "Graphics"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class BeginPath
	 * @constructor
	 **/
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.BeginPath = function() {
	}).prototype.exec = function(ctx) { ctx.beginPath(); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/beginFill"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class Fill
	 * @constructor
	 * @param {Object} style A valid Context2D fillStyle.
	 * @param {Matrix2D} matrix
	 **/
	/**
	 * A valid Context2D fillStyle.
	 * @property style
	 * @type Object
	 */
	/**
	 * @property matrix
	 * @type Matrix2D
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	p = (G.Fill = function(style, matrix) {
		this.style = style;
		this.matrix = matrix;
	}).prototype;
	p.exec = function(ctx) {
		if (!this.style) { return; }
		ctx.fillStyle = this.style;
		var mtx = this.matrix;
		if (mtx) { ctx.save(); ctx.transform(mtx.a, mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty); }
		ctx.fill();
		if (mtx) { ctx.restore(); }
	};
	/**
	 * Creates a linear gradient style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
	 * See {{#crossLink "Graphics/beginLinearGradientFill"}}{{/crossLink}} for more information.
	 * @method linearGradient
	 * @param {Array} colors
	 *
	 * @param {Array} ratios
	 * @param {Number} x0
	 * @param {Number} y0
	 * @param {Number} x1
	 * @param {Number} y1
	 * @return {Fill} Returns this Fill object for chaining or assignment.
	 */
	p.linearGradient = function(colors, ratios, x0, y0, x1, y1) {
		var o = this.style =  Graphics._ctx.createLinearGradient(x0, y0, x1, y1);
		for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); }
		o.props = {colors:colors, ratios:ratios, x0:x0, y0:y0, x1:x1, y1:y1, type:"linear"};
		return this;
	};
	/**
	 * Creates a radial gradient style and assigns it to {{#crossLink "Fill/style:property"}}{{/crossLink}}.
	 * See {{#crossLink "Graphics/beginRadialGradientFill"}}{{/crossLink}} for more information.
	 * @method radialGradient
	 * @param {Array} colors
	 * @param {Array} ratios
	 * @param {Number} x0
	 * @param {Number} y0
	 * @param {Number} r0
	 * @param {Number} x1
	 * @param {Number} y1
	 * @param {Number} r1
	 * @return {Fill} Returns this Fill object for chaining or assignment.
	 */
	p.radialGradient = function(colors, ratios, x0, y0, r0, x1, y1, r1) {
		var o = this.style =  Graphics._ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
		for (var i=0, l=colors.length; i<l; i++) { o.addColorStop(ratios[i], colors[i]); }
		o.props = {colors:colors, ratios:ratios, x0:x0, y0:y0, r0:r0, x1:x1, y1:y1, r1:r1, type:"radial"};
		return this;
	};
	/**
	 * Creates a bitmap fill style and assigns it to the {{#crossLink "Fill/style:property"}}{{/crossLink}}.
	 * See {{#crossLink "Graphics/beginBitmapFill"}}{{/crossLink}} for more information.
	 * @method bitmap
	 * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement} image  Must be loaded prior to creating a bitmap fill, or the fill will be empty.
	 * @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
	 * @return {Fill} Returns this Fill object for chaining or assignment.
	 */
	p.bitmap = function(image, repetition) {
		if (image.naturalWidth || image.getContext || image.readyState >= 2) {
			var o = this.style = Graphics._ctx.createPattern(image, repetition || "");
			o.props = {image: image, repetition: repetition, type: "bitmap"};
		}
		return this;
	};
	p.path = false;

	/**
	 * Graphics command object. See {{#crossLink "Graphics/beginStroke"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class Stroke
	 * @constructor
	 * @param {Object} style A valid Context2D fillStyle.
	 * @param {Boolean} ignoreScale
	 **/
	/**
	 * A valid Context2D strokeStyle.
	 * @property style
	 * @type Object
	 */
	/**
	 * @property ignoreScale
	 * @type Boolean
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	p = (G.Stroke = function(style, ignoreScale) {
		this.style = style;
		this.ignoreScale = ignoreScale;
	}).prototype;
	p.exec = function(ctx) {
		if (!this.style) { return; }
		ctx.strokeStyle = this.style;
		if (this.ignoreScale) { ctx.save(); ctx.setTransform(1,0,0,1,0,0); }
		ctx.stroke();
		if (this.ignoreScale) { ctx.restore(); }
	};
	/**
	 * Creates a linear gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
	 * See {{#crossLink "Graphics/beginLinearGradientStroke"}}{{/crossLink}} for more information.
	 * @method linearGradient
	 * @param {Array} colors
	 * @param {Array} ratios
	 * @param {Number} x0
	 * @param {Number} y0
	 * @param {Number} x1
	 * @param {Number} y1
	 * @return {Fill} Returns this Stroke object for chaining or assignment.
	 */
	p.linearGradient = G.Fill.prototype.linearGradient;
	/**
	 * Creates a radial gradient style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
	 * See {{#crossLink "Graphics/beginRadialGradientStroke"}}{{/crossLink}} for more information.
	 * @method radialGradient
	 * @param {Array} colors
	 * @param {Array} ratios
	 * @param {Number} x0
	 * @param {Number} y0
	 * @param {Number} r0
	 * @param {Number} x1
	 * @param {Number} y1
	 * @param {Number} r1
	 * @return {Fill} Returns this Stroke object for chaining or assignment.
	 */
	p.radialGradient = G.Fill.prototype.radialGradient;
	/**
	 * Creates a bitmap fill style and assigns it to {{#crossLink "Stroke/style:property"}}{{/crossLink}}.
	 * See {{#crossLink "Graphics/beginBitmapStroke"}}{{/crossLink}} for more information.
	 * @method bitmap
	 * @param {HTMLImageElement} image
	 * @param {String} [repetition] One of: repeat, repeat-x, repeat-y, or no-repeat.
	 * @return {Fill} Returns this Stroke object for chaining or assignment.
	 */
	p.bitmap = G.Fill.prototype.bitmap;
	p.path = false;

	/**
	 * Graphics command object. See {{#crossLink "Graphics/setStrokeStyle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class StrokeStyle
	 * @constructor
	 * @param {Number} width
	 * @param {String} [caps=butt]
	 * @param {String} [joints=miter]
	 * @param {Number} [miterLimit=10]
	 * @param {Boolean} [ignoreScale=false]
	 **/
	/**
	 * @property width
	 * @type Number
	 */
	/**
	 * One of: butt, round, square
	 * @property caps
	 * @type String
	 */
	/**
	 * One of: round, bevel, miter
	 * @property joints
	 * @type String
	 */
	/**
	 * @property miterLimit
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	p = (G.StrokeStyle = function(width, caps, joints, miterLimit, ignoreScale) {
		this.width = width;
		this.caps = caps;
		this.joints = joints;
		this.miterLimit = miterLimit;
		this.ignoreScale = ignoreScale;
	}).prototype;
	p.exec = function(ctx) {
		ctx.lineWidth = (this.width == null ? "1" : this.width);
		ctx.lineCap = (this.caps == null ? "butt" : (isNaN(this.caps) ? this.caps : Graphics.STROKE_CAPS_MAP[this.caps]));
		ctx.lineJoin = (this.joints == null ? "miter" : (isNaN(this.joints) ? this.joints : Graphics.STROKE_JOINTS_MAP[this.joints]));
		ctx.miterLimit = (this.miterLimit == null ? "10" : this.miterLimit);
		ctx.ignoreScale = (this.ignoreScale == null ? false : this.ignoreScale);
	};
	p.path = false;
	
	/**
	 * Graphics command object. See {{#crossLink "Graphics/setStrokeDash"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class StrokeDash
	 * @constructor
	 * @param {Array} [segments]
	 * @param {Number} [offset=0]
	 **/
	/**
	 * @property segments
	 * @type Array
	 */
	/**
	 * @property offset
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.StrokeDash = function(segments, offset) {
		this.segments = segments;
		this.offset = offset||0;
	}).prototype.exec = function(ctx) {
		if (ctx.setLineDash) { // feature detection.
			ctx.setLineDash(this.segments|| G.StrokeDash.EMPTY_SEGMENTS); // instead of [] to reduce churn.
			ctx.lineDashOffset = this.offset||0;
		}
	};
	/**
	 * The default value for segments (ie. no dash).
	 * @property EMPTY_SEGMENTS
	 * @static
	 * @final
	 * @readonly
	 * @protected
	 * @type {Array}
	 **/
	G.StrokeDash.EMPTY_SEGMENTS = [];

	/**
	 * Graphics command object. See {{#crossLink "Graphics/drawRoundRectComplex"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class RoundRect
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w
	 * @param {Number} h
	 * @param {Number} radiusTL
	 * @param {Number} radiusTR
	 * @param {Number} radiusBR
	 * @param {Number} radiusBL
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @property w
	 * @type Number
	 */
	/**
	 * @property h
	 * @type Number
	 */
	/**
	 * @property radiusTL
	 * @type Number
	 */
	/**
	 * @property radiusTR
	 * @type Number
	 */
	/**
	 * @property radiusBR
	 * @type Number
	 */
	/**
	 * @property radiusBL
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.RoundRect = function(x, y, w, h, radiusTL, radiusTR, radiusBR, radiusBL) {
		this.x = x; this.y = y;
		this.w = w; this.h = h;
		this.radiusTL = radiusTL; this.radiusTR = radiusTR;
		this.radiusBR = radiusBR; this.radiusBL = radiusBL;
	}).prototype.exec = function(ctx) {
		var max = (w<h?w:h)/2;
		var mTL=0, mTR=0, mBR=0, mBL=0;
		var x = this.x, y = this.y, w = this.w, h = this.h;
		var rTL = this.radiusTL, rTR = this.radiusTR, rBR = this.radiusBR, rBL = this.radiusBL;

		if (rTL < 0) { rTL *= (mTL=-1); }
		if (rTL > max) { rTL = max; }
		if (rTR < 0) { rTR *= (mTR=-1); }
		if (rTR > max) { rTR = max; }
		if (rBR < 0) { rBR *= (mBR=-1); }
		if (rBR > max) { rBR = max; }
		if (rBL < 0) { rBL *= (mBL=-1); }
		if (rBL > max) { rBL = max; }

		ctx.moveTo(x+w-rTR, y);
		ctx.arcTo(x+w+rTR*mTR, y-rTR*mTR, x+w, y+rTR, rTR);
		ctx.lineTo(x+w, y+h-rBR);
		ctx.arcTo(x+w+rBR*mBR, y+h+rBR*mBR, x+w-rBR, y+h, rBR);
		ctx.lineTo(x+rBL, y+h);
		ctx.arcTo(x-rBL*mBL, y+h+rBL*mBL, x, y+h-rBL, rBL);
		ctx.lineTo(x, y+rTL);
		ctx.arcTo(x-rTL*mTL, y-rTL*mTL, x+rTL, y, rTL);
		ctx.closePath();
	};

	/**
	 * Graphics command object. See {{#crossLink "Graphics/drawCircle"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class Circle
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} radius
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @property radius
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.Circle = function(x, y, radius) {
		this.x = x; this.y = y;
		this.radius = radius;
	}).prototype.exec = function(ctx) { ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2); };

	/**
	 * Graphics command object. See {{#crossLink "Graphics/drawEllipse"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class Ellipse
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} w
	 * @param {Number} h
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @property w
	 * @type Number
	 */
	/**
	 * @property h
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.Ellipse = function(x, y, w, h) {
		this.x = x; this.y = y;
		this.w = w; this.h = h;
	}).prototype.exec = function(ctx) {
		var x = this.x, y = this.y;
		var w = this.w, h = this.h;

		var k = 0.5522848;
		var ox = (w / 2) * k;
		var oy = (h / 2) * k;
		var xe = x + w;
		var ye = y + h;
		var xm = x + w / 2;
		var ym = y + h / 2;

		ctx.moveTo(x, ym);
		ctx.bezierCurveTo(x, ym-oy, xm-ox, y, xm, y);
		ctx.bezierCurveTo(xm+ox, y, xe, ym-oy, xe, ym);
		ctx.bezierCurveTo(xe, ym+oy, xm+ox, ye, xm, ye);
		ctx.bezierCurveTo(xm-ox, ye, x, ym+oy, x, ym);
	};

	/**
	 * Graphics command object. See {{#crossLink "Graphics/drawPolyStar"}}{{/crossLink}} and {{#crossLink "Graphics/append"}}{{/crossLink}} for more information.
	 * @class PolyStar
	 * @constructor
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Number} radius
	 * @param {Number} sides
	 * @param {Number} pointSize
	 * @param {Number} angle
	 **/
	/**
	 * @property x
	 * @type Number
	 */
	/**
	 * @property y
	 * @type Number
	 */
	/**
	 * @property radius
	 * @type Number
	 */
	/**
	 * @property sides
	 * @type Number
	 */
	/**
	 * @property pointSize
	 * @type Number
	 */
	/**
	 * @property angle
	 * @type Number
	 */
	/**
	 * Execute the Graphics command in the provided Canvas context.
	 * @method exec
	 * @param {CanvasRenderingContext2D} ctx The canvas rendering context
	 */
	(G.PolyStar = function(x, y, radius, sides, pointSize, angle) {
		this.x = x; this.y = y;
		this.radius = radius;
		this.sides = sides;
		this.pointSize = pointSize;
		this.angle = angle;
	}).prototype.exec = function(ctx) {
		var x = this.x, y = this.y;
		var radius = this.radius;
		var angle = (this.angle||0)/180*Math.PI;
		var sides = this.sides;
		var ps = 1-(this.pointSize||0);
		var a = Math.PI/sides;

		ctx.moveTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius);
		for (var i=0; i<sides; i++) {
			angle += a;
			if (ps != 1) {
				ctx.lineTo(x+Math.cos(angle)*radius*ps, y+Math.sin(angle)*radius*ps);
			}
			angle += a;
			ctx.lineTo(x+Math.cos(angle)*radius, y+Math.sin(angle)*radius);
		}
		ctx.closePath();
	};

	// docced above.
	Graphics.beginCmd = new G.BeginPath(); // so we don't have to instantiate multiple instances.


	createjs.Graphics = Graphics;
}());

//##############################################################################
// DisplayObject.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * DisplayObject is an abstract class that should not be constructed directly. Instead construct subclasses such as
	 * {{#crossLink "Container"}}{{/crossLink}}, {{#crossLink "Bitmap"}}{{/crossLink}}, and {{#crossLink "Shape"}}{{/crossLink}}.
	 * DisplayObject is the base class for all display classes in the EaselJS library. It defines the core properties and
	 * methods that are shared between all display objects, such as transformation properties (x, y, scaleX, scaleY, etc),
	 * caching, and mouse handlers.
	 * @class DisplayObject
	 * @extends EventDispatcher
	 * @constructor
	 **/
	function DisplayObject() {
		this.EventDispatcher_constructor();
		
		
	// public properties:
		/**
		 * The alpha (transparency) for this display object. 0 is fully transparent, 1 is fully opaque.
		 * @property alpha
		 * @type {Number}
		 * @default 1
		 **/
		this.alpha = 1;
	
		/**
		 * If a cache is active, this returns the canvas that holds the cached version of this display object. See {{#crossLink "cache"}}{{/crossLink}}
		 * for more information.
		 * @property cacheCanvas
		 * @type {HTMLCanvasElement | Object}
		 * @default null
		 * @readonly
		 **/
		this.cacheCanvas = null;
	
		/**
		 * Returns an ID number that uniquely identifies the current cache for this display object. This can be used to
		 * determine if the cache has changed since a previous check.
		 * @property cacheID
		 * @type {Number}
		 * @default 0
		 */
		this.cacheID = 0;
	
		/**
		 * Unique ID for this display object. Makes display objects easier for some uses.
		 * @property id
		 * @type {Number}
		 * @default -1
		 **/
		this.id = createjs.UID.get();
	
		/**
		 * Indicates whether to include this object when running mouse interactions. Setting this to `false` for children
		 * of a {{#crossLink "Container"}}{{/crossLink}} will cause events on the Container to not fire when that child is
		 * clicked. Setting this property to `false` does not prevent the {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}
		 * method from returning the child.
		 *
		 * <strong>Note:</strong> In EaselJS 0.7.0, the mouseEnabled property will not work properly with nested Containers. Please
		 * check out the latest NEXT version in <a href="https://github.com/CreateJS/EaselJS/tree/master/lib">GitHub</a> for an updated version with this issue resolved. The fix will be
		 * provided in the next release of EaselJS.
		 * @property mouseEnabled
		 * @type {Boolean}
		 * @default true
		 **/
		this.mouseEnabled = true;
		
		/**
		 * If false, the tick will not run on this display object (or its children). This can provide some performance benefits.
		 * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
		 * on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
		 * @property tickEnabled
		 * @type Boolean
		 * @default true
		 **/
		this.tickEnabled = true;
	
		/**
		 * An optional name for this display object. Included in {{#crossLink "DisplayObject/toString"}}{{/crossLink}} . Useful for
		 * debugging.
		 * @property name
		 * @type {String}
		 * @default null
		 **/
		this.name = null;
	
		/**
		 * A reference to the {{#crossLink "Container"}}{{/crossLink}} or {{#crossLink "Stage"}}{{/crossLink}} object that
		 * contains this display object, or null if it has not been added
		 * to one.
		 * @property parent
		 * @final
		 * @type {Container}
		 * @default null
		 * @readonly
		 **/
		this.parent = null;
	
		/**
		 * The left offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate
		 * around its center, you would set regX and {{#crossLink "DisplayObject/regY:property"}}{{/crossLink}} to 50.
		 * @property regX
		 * @type {Number}
		 * @default 0
		 **/
		this.regX = 0;
	
		/**
		 * The y offset for this display object's registration point. For example, to make a 100x100px Bitmap rotate around
		 * its center, you would set {{#crossLink "DisplayObject/regX:property"}}{{/crossLink}} and regY to 50.
		 * @property regY
		 * @type {Number}
		 * @default 0
		 **/
		this.regY = 0;
	
		/**
		 * The rotation in degrees for this display object.
		 * @property rotation
		 * @type {Number}
		 * @default 0
		 **/
		this.rotation = 0;
	
		/**
		 * The factor to stretch this display object horizontally. For example, setting scaleX to 2 will stretch the display
		 * object to twice its nominal width. To horizontally flip an object, set the scale to a negative number.
		 * @property scaleX
		 * @type {Number}
		 * @default 1
		 **/
		this.scaleX = 1;
	
		/**
		 * The factor to stretch this display object vertically. For example, setting scaleY to 0.5 will stretch the display
		 * object to half its nominal height. To vertically flip an object, set the scale to a negative number.
		 * @property scaleY
		 * @type {Number}
		 * @default 1
		 **/
		this.scaleY = 1;
	
		/**
		 * The factor to skew this display object horizontally.
		 * @property skewX
		 * @type {Number}
		 * @default 0
		 **/
		this.skewX = 0;
	
		/**
		 * The factor to skew this display object vertically.
		 * @property skewY
		 * @type {Number}
		 * @default 0
		 **/
		this.skewY = 0;
	
		/**
		 * A shadow object that defines the shadow to render on this display object. Set to `null` to remove a shadow. If
		 * null, this property is inherited from the parent container.
		 * @property shadow
		 * @type {Shadow}
		 * @default null
		 **/
		this.shadow = null;
	
		/**
		 * Indicates whether this display object should be rendered to the canvas and included when running the Stage
		 * {{#crossLink "Stage/getObjectsUnderPoint"}}{{/crossLink}} method.
		 * @property visible
		 * @type {Boolean}
		 * @default true
		 **/
		this.visible = true;
	
		/**
		 * The x (horizontal) position of the display object, relative to its parent.
		 * @property x
		 * @type {Number}
		 * @default 0
		 **/
		this.x = 0;
	
		/** The y (vertical) position of the display object, relative to its parent.
		 * @property y
		 * @type {Number}
		 * @default 0
		 **/
		this.y = 0;
		
		/**
		 * If set, defines the transformation for this display object, overriding all other transformation properties
		 * (x, y, rotation, scale, skew).
		 * @property transformMatrix
		 * @type {Matrix2D}
		 * @default null
		 **/
		this.transformMatrix = null;
		
		/**
		 * The composite operation indicates how the pixels of this display object will be composited with the elements
		 * behind it. If `null`, this property is inherited from the parent container. For more information, read the
		 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">
		 * whatwg spec on compositing</a>.
		 * @property compositeOperation
		 * @type {String}
		 * @default null
		 **/
		this.compositeOperation = null;
	
		/**
		 * Indicates whether the display object should be drawn to a whole pixel when
		 * {{#crossLink "Stage/snapToPixelEnabled"}}{{/crossLink}} is true. To enable/disable snapping on whole
		 * categories of display objects, set this value on the prototype (Ex. Text.prototype.snapToPixel = true).
		 * @property snapToPixel
		 * @type {Boolean}
		 * @default true
		 **/
		this.snapToPixel = true;
	
		/**
		 * An array of Filter objects to apply to this display object. Filters are only applied / updated when {{#crossLink "cache"}}{{/crossLink}}
		 * or {{#crossLink "updateCache"}}{{/crossLink}} is called on the display object, and only apply to the area that is
		 * cached.
		 * @property filters
		 * @type {Array}
		 * @default null
		 **/
		this.filters = null;
		
		/**
		 * A Shape instance that defines a vector mask (clipping path) for this display object.  The shape's transformation
		 * will be applied relative to the display object's parent coordinates (as if it were a child of the parent).
		 * @property mask
		 * @type {Shape}
		 * @default null
		 */
		this.mask = null;
		
		/**
		 * A display object that will be tested when checking mouse interactions or testing {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}.
		 * The hit area will have its transformation applied relative to this display object's coordinate space (as though
		 * the hit test object were a child of this display object and relative to its regX/Y). The hitArea will be tested
		 * using only its own `alpha` value regardless of the alpha value on the target display object, or the target's
		 * ancestors (parents).
		 * 
		 * If set on a {{#crossLink "Container"}}{{/crossLink}}, children of the Container will not receive mouse events.
		 * This is similar to setting {{#crossLink "mouseChildren"}}{{/crossLink}} to false.
		 *
		 * Note that hitArea is NOT currently used by the `hitTest()` method, nor is it supported for {{#crossLink "Stage"}}{{/crossLink}}.
		 * @property hitArea
		 * @type {DisplayObject}
		 * @default null
		 */
		this.hitArea = null;
		
		/**
		 * A CSS cursor (ex. "pointer", "help", "text", etc) that will be displayed when the user hovers over this display
		 * object. You must enable mouseover events using the {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}} method to
		 * use this property. Setting a non-null cursor on a Container will override the cursor set on its descendants.
		 * @property cursor
		 * @type {String}
		 * @default null
		 */
		this.cursor = null;
	
	
	// private properties:
		/**
		 * @property _cacheOffsetX
		 * @protected
		 * @type {Number}
		 * @default 0
		 **/
		this._cacheOffsetX = 0;
	
		/**
		 * @property _cacheOffsetY
		 * @protected
		 * @type {Number}
		 * @default 0
		 **/
		this._cacheOffsetY = 0;
		
		/**
		 * @property _filterOffsetX
		 * @protected
		 * @type {Number}
		 * @default 0
		 **/
		this._filterOffsetX = 0;
		
		/**
		 * @property _filterOffsetY
		 * @protected
		 * @type {Number}
		 * @default 0
		 **/
		this._filterOffsetY = 0;
		
		/**
		 * @property _cacheScale
		 * @protected
		 * @type {Number}
		 * @default 1
		 **/
		this._cacheScale = 1;
	
		/**
		* @property _cacheDataURLID
		* @protected
		* @type {Number}
		* @default 0
		*/
		this._cacheDataURLID = 0;
		
		/**
		* @property _cacheDataURL
		* @protected
		* @type {String}
		* @default null
		*/
		this._cacheDataURL = null;
	
		/**
		 * @property _props
		 * @protected
		 * @type {DisplayObject}
		 * @default null
		 **/
		this._props = new createjs.DisplayProps();
	
		/**
		 * @property _rectangle
		 * @protected
		 * @type {Rectangle}
		 * @default null
		 **/
		this._rectangle = new createjs.Rectangle();
	
		/**
		 * @property _bounds
		 * @protected
		 * @type {Rectangle}
		 * @default null
		 **/
		this._bounds = null;
	}
	var p = createjs.extend(DisplayObject, createjs.EventDispatcher);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
	
// static properties:
	/**
	 * Listing of mouse event names. Used in _hasMouseEventListener.
	 * @property _MOUSE_EVENTS
	 * @protected
	 * @static
	 * @type {Array}
	 **/
	DisplayObject._MOUSE_EVENTS = ["click","dblclick","mousedown","mouseout","mouseover","pressmove","pressup","rollout","rollover"];

	/**
	 * Suppresses errors generated when using features like hitTest, mouse events, and {{#crossLink "getObjectsUnderPoint"}}{{/crossLink}}
	 * with cross domain content.
	 * @property suppressCrossDomainErrors
	 * @static
	 * @type {Boolean}
	 * @default false
	 **/
	DisplayObject.suppressCrossDomainErrors = false;
	
	/**
	 * @property _snapToPixelEnabled
	 * @protected
	 * @static
	 * @type {Boolean}
	 * @default false
	 **/
	DisplayObject._snapToPixelEnabled = false; // stage.snapToPixelEnabled is temporarily copied here during a draw to provide global access.

	/**
	 * @property _hitTestCanvas
	 * @type {HTMLCanvasElement | Object}
	 * @static
	 * @protected
	 **/
	/**
	 * @property _hitTestContext
	 * @type {CanvasRenderingContext2D}
	 * @static
	 * @protected
	 **/
	var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); // prevent errors on load in browsers without canvas.
	if (canvas.getContext) {
		DisplayObject._hitTestCanvas = canvas;
		DisplayObject._hitTestContext = canvas.getContext("2d");
		canvas.width = canvas.height = 1;
	}

	/**
	 * @property _nextCacheID
	 * @type {Number}
	 * @static
	 * @protected
	 **/
	DisplayObject._nextCacheID = 1;


// events:
	/**
	 * Dispatched when the user presses their left mouse button over the display object. See the 
	 * {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event mousedown
	 * @since 0.6.0
	 */
	 
	/**
	 * Dispatched when the user presses their left mouse button and then releases it while over the display object.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event click
	 * @since 0.6.0
	 */
	 
	/**
	 * Dispatched when the user double clicks their left mouse button over this display object.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event dblclick
	 * @since 0.6.0
	 */
	 
	/**
	 * Dispatched when the user's mouse enters this display object. This event must be enabled using 
	 * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event mouseover
	 * @since 0.6.0
	 */

	/**
	 * Dispatched when the user's mouse leaves this display object. This event must be enabled using 
	 * {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}. See also {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event mouseout
	 * @since 0.6.0
	 */
	 
	/**
	 * This event is similar to {{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}, with the following
	 * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
	 * aggregate of their content.
	 * 
	 * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
	 * shapeA and then directly on to shapeB. With a listener for {{#crossLink "mouseover:event"}}{{/crossLink}} on
	 * myContainer, two events would be received, each targeting a child element:<OL>
	 * <LI>when the mouse enters shapeA (target=shapeA)</LI>
	 * <LI>when the mouse enters shapeB (target=shapeB)</LI>
	 * </OL>
	 * However, with a listener for "rollover" instead, only a single event is received when the mouse first enters
	 * the aggregate myContainer content (target=myContainer).
	 * 
	 * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event rollover
	 * @since 0.7.0
	 */
	 
	/**
	 * This event is similar to {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}, with the following
	 * differences: it does not bubble, and it considers {{#crossLink "Container"}}{{/crossLink}} instances as an
	 * aggregate of their content.
	 * 
	 * For example, myContainer contains two overlapping children: shapeA and shapeB. The user moves their mouse over
	 * shapeA, then directly on to shapeB, then off both. With a listener for {{#crossLink "mouseout:event"}}{{/crossLink}}
	 * on myContainer, two events would be received, each targeting a child element:<OL>
	 * <LI>when the mouse leaves shapeA (target=shapeA)</LI>
	 * <LI>when the mouse leaves shapeB (target=shapeB)</LI>
	 * </OL>
	 * However, with a listener for "rollout" instead, only a single event is received when the mouse leaves
	 * the aggregate myContainer content (target=myContainer).
	 * 
	 * This event must be enabled using {{#crossLink "Stage/enableMouseOver"}}{{/crossLink}}.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event rollout
	 * @since 0.7.0
	 */
	 
	/**
	 * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressmove
	 * event will be generated on that object whenever the mouse moves until the mouse press is released. This can be
	 * useful for dragging and similar operations.
	 * @event pressmove
	 * @since 0.7.0
	 */
	 
	/**
	 * After a {{#crossLink "DisplayObject/mousedown:event"}}{{/crossLink}} occurs on a display object, a pressup event
	 * will be generated on that object when that mouse press is released. This can be useful for dragging and similar
	 * operations.
	 * @event pressup
	 * @since 0.7.0
	 */
	 
	/**
	 * Dispatched when the display object is added to a parent container.
	 * @event added
	 */
	 
	/**
	 * Dispatched when the display object is removed from its parent container.
	 * @event removed
	 */
	 
	/**
	 * Dispatched on each display object on a stage whenever the stage updates. This occurs immediately before the
	 * rendering (draw) pass. When {{#crossLink "Stage/update"}}{{/crossLink}} is called, first all display objects on
	 * the stage dispatch the tick event, then all of the display objects are drawn to stage. Children will have their
	 * {{#crossLink "tick:event"}}{{/crossLink}} event dispatched in order of their depth prior to the event being
	 * dispatched on their parent.
	 * @event tick
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {Array} params An array containing any arguments that were passed to the Stage.update() method. For
	 *      example if you called stage.update("hello"), then the params would be ["hello"].
	 * @since 0.6.0
	 */
	
	
// getter / setters:
	/**
	 * Use the {{#crossLink "DisplayObject/stage:property"}}{{/crossLink}} property instead.
	 * @method getStage
	 * @return {Stage}
	 * @deprecated
	 **/
	p.getStage = function() {
		// uses dynamic access to avoid circular dependencies;
		var o = this, _Stage = createjs["Stage"];
		while (o.parent) { o = o.parent; }
		if (o instanceof _Stage) { return o; }
		return null;
	};

	/**
	 * Returns the Stage instance that this display object will be rendered on, or null if it has not been added to one.
	 * @property stage
	 * @type {Stage}
	 * @readonly
	 **/
	try {
		Object.defineProperties(p, {
			stage: { get: p.getStage }
		});
	} catch (e) {}


// public methods:
	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns <code>true</code> if the draw was handled (useful for overriding functionality).
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
	 * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
	 * @return {Boolean}
	 **/
	p.draw = function(ctx, ignoreCache) {
		var cacheCanvas = this.cacheCanvas;
		if (ignoreCache || !cacheCanvas) { return false; }
		var scale = this._cacheScale;
		ctx.drawImage(cacheCanvas, this._cacheOffsetX+this._filterOffsetX, this._cacheOffsetY+this._filterOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale);
		return true;
	};
	
	/**
	 * Applies this display object's transformation, alpha, globalCompositeOperation, clipping path (mask), and shadow
	 * to the specified context. This is typically called prior to {{#crossLink "DisplayObject/draw"}}{{/crossLink}}.
	 * @method updateContext
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D to update.
	 **/
	p.updateContext = function(ctx) {
		var o=this, mask=o.mask, mtx= o._props.matrix;
		
		if (mask && mask.graphics && !mask.graphics.isEmpty()) {
			mask.getMatrix(mtx);
			ctx.transform(mtx.a,  mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
			
			mask.graphics.drawAsPath(ctx);
			ctx.clip();
			
			mtx.invert();
			ctx.transform(mtx.a,  mtx.b, mtx.c, mtx.d, mtx.tx, mtx.ty);
		}
		
		this.getMatrix(mtx);
		var tx = mtx.tx, ty = mtx.ty;
		if (DisplayObject._snapToPixelEnabled && o.snapToPixel) {
			tx = tx + (tx < 0 ? -0.5 : 0.5) | 0;
			ty = ty + (ty < 0 ? -0.5 : 0.5) | 0;
		}
		ctx.transform(mtx.a,  mtx.b, mtx.c, mtx.d, tx, ty);
		ctx.globalAlpha *= o.alpha;
		if (o.compositeOperation) { ctx.globalCompositeOperation = o.compositeOperation; }
		if (o.shadow) { this._applyShadow(ctx, o.shadow); }
	};

	/**
	 * Draws the display object into a new canvas, which is then used for subsequent draws. For complex content
	 * that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape),
	 * this can provide for much faster rendering because the content does not need to be re-rendered each tick. The
	 * cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must
	 * manually update the cache by calling <code>updateCache()</code> or <code>cache()</code> again. You must specify
	 * the cache area via the x, y, w, and h parameters. This defines the rectangle that will be rendered and cached
	 * using this display object's coordinates.
	 *
	 * <h4>Example</h4>
	 * For example if you defined a Shape that drew a circle at 0, 0 with a radius of 25:
	 *
	 *      var shape = new createjs.Shape();
	 *      shape.graphics.beginFill("#ff0000").drawCircle(0, 0, 25);
	 *      myShape.cache(-25, -25, 50, 50);
	 *
	 * Note that filters need to be defined <em>before</em> the cache is applied. Check out the {{#crossLink "Filter"}}{{/crossLink}}
	 * class for more information. Some filters (ex. BlurFilter) will not work as expected in conjunction with the scale param.
	 * 
	 * Usually, the resulting cacheCanvas will have the dimensions width*scale by height*scale, however some filters (ex. BlurFilter)
	 * will add padding to the canvas dimensions.
	 *
	 * @method cache
	 * @param {Number} x The x coordinate origin for the cache region.
	 * @param {Number} y The y coordinate origin for the cache region.
	 * @param {Number} width The width of the cache region.
	 * @param {Number} height The height of the cache region.
	 * @param {Number} [scale=1] The scale at which the cache will be created. For example, if you cache a vector shape using
	 * 	myShape.cache(0,0,100,100,2) then the resulting cacheCanvas will be 200x200 px. This lets you scale and rotate
	 * 	cached elements with greater fidelity. Default is 1.
	 **/
	p.cache = function(x, y, width, height, scale) {
		// draw to canvas.
		scale = scale||1;
		if (!this.cacheCanvas) { this.cacheCanvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
		this._cacheWidth = width;
		this._cacheHeight = height;
		this._cacheOffsetX = x;
		this._cacheOffsetY = y;
		this._cacheScale = scale;
		this.updateCache();
	};

	/**
	 * Redraws the display object to its cache. Calling updateCache without an active cache will throw an error.
	 * If compositeOperation is null the current cache will be cleared prior to drawing. Otherwise the display object
	 * will be drawn over the existing cache using the specified compositeOperation.
	 *
	 * <h4>Example</h4>
	 * Clear the current graphics of a cached shape, draw some new instructions, and then update the cache. The new line
	 * will be drawn on top of the old one.
	 *
	 *      // Not shown: Creating the shape, and caching it.
	 *      shapeInstance.clear();
	 *      shapeInstance.setStrokeStyle(3).beginStroke("#ff0000").moveTo(100, 100).lineTo(200,200);
	 *      shapeInstance.updateCache();
	 *
	 * @method updateCache
	 * @param {String} compositeOperation The compositeOperation to use, or null to clear the cache and redraw it.
	 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#compositing">
	 * whatwg spec on compositing</a>.
	 **/
	p.updateCache = function(compositeOperation) {
		var cacheCanvas = this.cacheCanvas;
		if (!cacheCanvas) { throw "cache() must be called before updateCache()"; }
		var scale = this._cacheScale, offX = this._cacheOffsetX*scale, offY = this._cacheOffsetY*scale;
		var w = this._cacheWidth, h = this._cacheHeight, ctx = cacheCanvas.getContext("2d");
		
		var fBounds = this._getFilterBounds();
		offX += (this._filterOffsetX = fBounds.x);
		offY += (this._filterOffsetY = fBounds.y);
		
		w = Math.ceil(w*scale) + fBounds.width;
		h = Math.ceil(h*scale) + fBounds.height;
		if (w != cacheCanvas.width || h != cacheCanvas.height) {
			// TODO: it would be nice to preserve the content if there is a compositeOperation.
			cacheCanvas.width = w;
			cacheCanvas.height = h;
		} else if (!compositeOperation) {
			ctx.clearRect(0, 0, w+1, h+1);
		}
		
		ctx.save();
		ctx.globalCompositeOperation = compositeOperation;
		ctx.setTransform(scale, 0, 0, scale, -offX, -offY);
		this.draw(ctx, true);
		// TODO: filters and cache scale don't play well together at present.
		this._applyFilters();
		ctx.restore();
		this.cacheID = DisplayObject._nextCacheID++;
	};

	/**
	 * Clears the current cache. See {{#crossLink "DisplayObject/cache"}}{{/crossLink}} for more information.
	 * @method uncache
	 **/
	p.uncache = function() {
		this._cacheDataURL = this.cacheCanvas = null;
		this.cacheID = this._cacheOffsetX = this._cacheOffsetY = this._filterOffsetX = this._filterOffsetY = 0;
		this._cacheScale = 1;
	};
	
	/**
	 * Returns a data URL for the cache, or null if this display object is not cached.
	 * Uses cacheID to ensure a new data URL is not generated if the cache has not changed.
	 * @method getCacheDataURL
	 * @return {String} The image data url for the cache.
	 **/
	p.getCacheDataURL = function() {
		if (!this.cacheCanvas) { return null; }
		if (this.cacheID != this._cacheDataURLID) { this._cacheDataURL = this.cacheCanvas.toDataURL(); }
		return this._cacheDataURL;
	};

	/**
	 * Transforms the specified x and y position from the coordinate space of the display object
	 * to the global (stage) coordinate space. For example, this could be used to position an HTML label
	 * over a specific point on a nested display object. Returns a Point instance with x and y properties
	 * correlating to the transformed coordinates on the stage.
	 *
	 * <h4>Example</h4>
	 *
	 *      displayObject.x = 300;
	 *      displayObject.y = 200;
	 *      stage.addChild(displayObject);
	 *      var point = displayObject.localToGlobal(100, 100);
	 *      // Results in x=400, y=300
	 *
	 * @method localToGlobal
	 * @param {Number} x The x position in the source display object to transform.
	 * @param {Number} y The y position in the source display object to transform.
	 * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. 
	 * @return {Point} A Point instance with x and y properties correlating to the transformed coordinates
	 * on the stage.
	 **/
	p.localToGlobal = function(x, y, pt) {
		return this.getConcatenatedMatrix(this._props.matrix).transformPoint(x,y, pt||new createjs.Point());
	};

	/**
	 * Transforms the specified x and y position from the global (stage) coordinate space to the
	 * coordinate space of the display object. For example, this could be used to determine
	 * the current mouse position within the display object. Returns a Point instance with x and y properties
	 * correlating to the transformed position in the display object's coordinate space.
	 *
	 * <h4>Example</h4>
	 *
	 *      displayObject.x = 300;
	 *      displayObject.y = 200;
	 *      stage.addChild(displayObject);
	 *      var point = displayObject.globalToLocal(100, 100);
	 *      // Results in x=-200, y=-100
	 *
	 * @method globalToLocal
	 * @param {Number} x The x position on the stage to transform.
	 * @param {Number} y The y position on the stage to transform.
	 * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. 
	 * @return {Point} A Point instance with x and y properties correlating to the transformed position in the
	 * display object's coordinate space.
	 **/
	p.globalToLocal = function(x, y, pt) {
		return this.getConcatenatedMatrix(this._props.matrix).invert().transformPoint(x,y, pt||new createjs.Point());
	};

	/**
	 * Transforms the specified x and y position from the coordinate space of this display object to the coordinate
	 * space of the target display object. Returns a Point instance with x and y properties correlating to the
	 * transformed position in the target's coordinate space. Effectively the same as using the following code with
	 * {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}} and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
	 *
	 *      var pt = this.localToGlobal(x, y);
	 *      pt = target.globalToLocal(pt.x, pt.y);
	 *
	 * @method localToLocal
	 * @param {Number} x The x position in the source display object to transform.
	 * @param {Number} y The y position on the source display object to transform.
	 * @param {DisplayObject} target The target display object to which the coordinates will be transformed.
	 * @param {Point | Object} [pt] An object to copy the result into. If omitted a new Point object with x/y properties will be returned. 
	 * @return {Point} Returns a Point instance with x and y properties correlating to the transformed position
	 * in the target's coordinate space.
	 **/
	p.localToLocal = function(x, y, target, pt) {
		pt = this.localToGlobal(x, y, pt);
		return target.globalToLocal(pt.x, pt.y, pt);
	};

	/**
	 * Shortcut method to quickly set the transform properties on the display object. All parameters are optional.
	 * Omitted parameters will have the default value set.
	 *
	 * <h4>Example</h4>
	 *
	 *      displayObject.setTransform(100, 100, 2, 2);
	 *
	 * @method setTransform
	 * @param {Number} [x=0] The horizontal translation (x position) in pixels
	 * @param {Number} [y=0] The vertical translation (y position) in pixels
	 * @param {Number} [scaleX=1] The horizontal scale, as a percentage of 1
	 * @param {Number} [scaleY=1] the vertical scale, as a percentage of 1
	 * @param {Number} [rotation=0] The rotation, in degrees
	 * @param {Number} [skewX=0] The horizontal skew factor
	 * @param {Number} [skewY=0] The vertical skew factor
	 * @param {Number} [regX=0] The horizontal registration point in pixels
	 * @param {Number} [regY=0] The vertical registration point in pixels
	 * @return {DisplayObject} Returns this instance. Useful for chaining commands.
	 * @chainable
	*/
	p.setTransform = function(x, y, scaleX, scaleY, rotation, skewX, skewY, regX, regY) {
		this.x = x || 0;
		this.y = y || 0;
		this.scaleX = scaleX == null ? 1 : scaleX;
		this.scaleY = scaleY == null ? 1 : scaleY;
		this.rotation = rotation || 0;
		this.skewX = skewX || 0;
		this.skewY = skewY || 0;
		this.regX = regX || 0;
		this.regY = regY || 0;
		return this;
	};
	
	/**
	 * Returns a matrix based on this object's current transform.
	 * @method getMatrix
	 * @param {Matrix2D} matrix Optional. A Matrix2D object to populate with the calculated values. If null, a new
	 * Matrix object is returned.
	 * @return {Matrix2D} A matrix representing this display object's transform.
	 **/
	p.getMatrix = function(matrix) {
		var o = this, mtx = matrix&&matrix.identity() || new createjs.Matrix2D();
		return o.transformMatrix ?  mtx.copy(o.transformMatrix) : mtx.appendTransform(o.x, o.y, o.scaleX, o.scaleY, o.rotation, o.skewX, o.skewY, o.regX, o.regY);
	};
	
	/**
	 * Generates a Matrix2D object representing the combined transform of the display object and all of its
	 * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}). This can
	 * be used to transform positions between coordinate spaces, such as with {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
	 * and {{#crossLink "DisplayObject/globalToLocal"}}{{/crossLink}}.
	 * @method getConcatenatedMatrix
	 * @param {Matrix2D} [matrix] A {{#crossLink "Matrix2D"}}{{/crossLink}} object to populate with the calculated values.
	 * If null, a new Matrix2D object is returned.
	 * @return {Matrix2D} The combined matrix.
	 **/
	p.getConcatenatedMatrix = function(matrix) {
		var o = this, mtx = this.getMatrix(matrix);
		while (o = o.parent) {
			mtx.prependMatrix(o.getMatrix(o._props.matrix));
		}
		return mtx;
	};
	
	/**
	 * Generates a DisplayProps object representing the combined display properties of the  object and all of its
	 * parent Containers up to the highest level ancestor (usually the {{#crossLink "Stage"}}{{/crossLink}}).
	 * @method getConcatenatedDisplayProps
	 * @param {DisplayProps} [props] A {{#crossLink "DisplayProps"}}{{/crossLink}} object to populate with the calculated values.
	 * If null, a new DisplayProps object is returned.
	 * @return {DisplayProps} The combined display properties.
	 **/
	p.getConcatenatedDisplayProps = function(props) {
		props = props ? props.identity() : new createjs.DisplayProps();
		var o = this, mtx = o.getMatrix(props.matrix); 
		do {
			props.prepend(o.visible, o.alpha, o.shadow, o.compositeOperation);
			
			// we do this to avoid problems with the matrix being used for both operations when o._props.matrix is passed in as the props param.
			// this could be simplified (ie. just done as part of the prepend above) if we switched to using a pool.
			if (o != this) { mtx.prependMatrix(o.getMatrix(o._props.matrix)); }
		} while (o = o.parent);
		return props;
	};

	/**
	 * Tests whether the display object intersects the specified point in <em>local</em> coordinates (ie. draws a pixel
	 * with alpha > 0 at the specified position). This ignores the alpha, shadow, hitArea, mask, and compositeOperation
	 * of the display object.
	 *
	 * <h4>Example</h4>
	 *
	 * 		var myShape = new createjs.Shape();
	 * 		myShape.graphics.beginFill("red").drawRect(100, 100, 20, 50);
	 *
	 * 		console.log(myShape.hitTest(10,10); // false
	 * 		console.log(myShape.hitTest(110, 25); // true
	 *
	 * Note that to use Stage coordinates (such as {{#crossLink "Stage/mouseX:property"}}{{/crossLink}}), they must
	 * first be converted to local coordinates:
	 *
	 *      stage.addEventListener("stagemousedown", handleMouseDown);
	 *      function handleMouseDown(event) {
	 *      	var p = myShape.globalToLocal(stage.mouseX, stage.mouseY);
	 *          var hit = myShape.hitTest(p.x, p.y);
	 *      }
	 *
	 * Shape-to-shape collision is not currently supported by EaselJS.
	 *
	 * @method hitTest
	 * @param {Number} x The x position to check in the display object's local coordinates.
	 * @param {Number} y The y position to check in the display object's local coordinates.
	 * @return {Boolean} A Boolean indicating whether a visible portion of the DisplayObject intersect the specified
	 * local Point.
	*/
	p.hitTest = function(x, y) {
		var ctx = DisplayObject._hitTestContext;
		ctx.setTransform(1, 0, 0, 1, -x, -y);
		this.draw(ctx);

		var hit = this._testHit(ctx);
		ctx.setTransform(1, 0, 0, 1, 0, 0);
		ctx.clearRect(0, 0, 2, 2);
		return hit;
	};
	
	/**
	 * Provides a chainable shortcut method for setting a number of properties on the instance.
	 *
	 * <h4>Example</h4>
	 *
	 *      var myGraphics = new createjs.Graphics().beginFill("#ff0000").drawCircle(0, 0, 25);
	 *      var shape = stage.addChild(new createjs.Shape()).set({graphics:myGraphics, x:100, y:100, alpha:0.5});
	 *
	 * @method set
	 * @param {Object} props A generic object containing properties to copy to the DisplayObject instance.
	 * @return {DisplayObject} Returns the instance the method is called on (useful for chaining calls.)
	 * @chainable
	*/
	p.set = function(props) {
		for (var n in props) { this[n] = props[n]; }
		return this;
	};
	
	/**
	 * Returns a rectangle representing this object's bounds in its local coordinate system (ie. with no transformation).
	 * Objects that have been cached will return the bounds of the cache.
	 * 
	 * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use 
	 * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
	 * bounds.
	 * 
	 * <table>
	 * 	<tr><td><b>All</b></td><td>
	 * 		All display objects support setting bounds manually using setBounds(). Likewise, display objects that
	 * 		have been cached using cache() will return the bounds of their cache. Manual and cache bounds will override
	 * 		the automatic calculations listed below.
	 * 	</td></tr>
	 * 	<tr><td><b>Bitmap</b></td><td>
	 * 		Returns the width and height of the sourceRect (if specified) or image, extending from (x=0,y=0).
	 * 	</td></tr>
	 * 	<tr><td><b>Sprite</b></td><td>
	 * 		Returns the bounds of the current frame. May have non-zero x/y if a frame registration point was specified
	 * 		in the spritesheet data. See also {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}}
	 * 	</td></tr>
	 * 	<tr><td><b>Container</b></td><td>
	 * 		Returns the aggregate (combined) bounds of all children that return a non-null value from getBounds().
	 * 	</td></tr>
	 * 	<tr><td><b>Shape</b></td><td>
	 * 		Does not currently support automatic bounds calculations. Use setBounds() to manually define bounds.
	 * 	</td></tr>
	 * 	<tr><td><b>Text</b></td><td>
	 * 		Returns approximate bounds. Horizontal values (x/width) are quite accurate, but vertical values (y/height) are
	 * 		not, especially when using textBaseline values other than "top".
	 * 	</td></tr>
	 * 	<tr><td><b>BitmapText</b></td><td>
	 * 		Returns approximate bounds. Values will be more accurate if spritesheet frame registration points are close
	 * 		to (x=0,y=0).
	 * 	</td></tr>
	* </table>
	 * 
	 * Bounds can be expensive to calculate for some objects (ex. text, or containers with many children), and
	 * are recalculated each time you call getBounds(). You can prevent recalculation on static objects by setting the
	 * bounds explicitly:
	 * 
	 * 	var bounds = obj.getBounds();
	 * 	obj.setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
	 * 	// getBounds will now use the set values, instead of recalculating
	 * 
	 * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
	 * values if you need to retain it.
	 * 
	 * 	var myBounds = obj.getBounds().clone();
	 * 	// OR:
	 * 	myRect.copy(obj.getBounds());
	 * 
	 * @method getBounds
	 * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this
	 * object.
	 **/
	p.getBounds = function() {
		if (this._bounds) { return this._rectangle.copy(this._bounds); }
		var cacheCanvas = this.cacheCanvas;
		if (cacheCanvas) {
			var scale = this._cacheScale;
			return this._rectangle.setValues(this._cacheOffsetX, this._cacheOffsetY, cacheCanvas.width/scale, cacheCanvas.height/scale);
		}
		return null;
	};
	
	/**
	 * Returns a rectangle representing this object's bounds in its parent's coordinate system (ie. with transformations applied).
	 * Objects that have been cached will return the transformed bounds of the cache.
	 * 
	 * Not all display objects can calculate their own bounds (ex. Shape). For these objects, you can use 
	 * {{#crossLink "DisplayObject/setBounds"}}{{/crossLink}} so that they are included when calculating Container
	 * bounds.
	 * 
	 * To reduce memory impact, the returned Rectangle instance may be reused internally; clone the instance or copy its
	 * values if you need to retain it.
	 * 
	 * Container instances calculate aggregate bounds for all children that return bounds via getBounds.
	 * @method getTransformedBounds
	 * @return {Rectangle} A Rectangle instance representing the bounds, or null if bounds are not available for this object.
	 **/
	p.getTransformedBounds = function() {
		return this._getBounds();
	};
	
	/**
	 * Allows you to manually specify the bounds of an object that either cannot calculate their own bounds (ex. Shape &
	 * Text) for future reference, or so the object can be included in Container bounds. Manually set bounds will always
	 * override calculated bounds.
	 * 
	 * The bounds should be specified in the object's local (untransformed) coordinates. For example, a Shape instance
	 * with a 25px radius circle centered at 0,0 would have bounds of (-25, -25, 50, 50).
	 * @method setBounds
	 * @param {Number} x The x origin of the bounds. Pass null to remove the manual bounds.
	 * @param {Number} y The y origin of the bounds.
	 * @param {Number} width The width of the bounds.
	 * @param {Number} height The height of the bounds.
	 **/
	p.setBounds = function(x, y, width, height) {
		if (x == null) { this._bounds = x; }
		this._bounds = (this._bounds || new createjs.Rectangle()).setValues(x, y, width, height);
	};

	/**
	 * Returns a clone of this DisplayObject. Some properties that are specific to this instance's current context are
	 * reverted to their defaults (for example .parent). Caches are not maintained across clones, and some elements
	 * are copied by reference (masks, individual filter instances, hit area)
	 * @method clone
	 * @return {DisplayObject} A clone of the current DisplayObject instance.
	 **/
	p.clone = function() {
		return this._cloneProps(new DisplayObject());
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[DisplayObject (name="+  this.name +")]";
	};


// private methods:
	// separated so it can be used more easily in subclasses:
	/**
	 * @method _cloneProps
	 * @param {DisplayObject} o The DisplayObject instance which will have properties from the current DisplayObject
	 * instance copied into.
	 * @return {DisplayObject} o
	 * @protected
	 **/
	p._cloneProps = function(o) {
		o.alpha = this.alpha;
		o.mouseEnabled = this.mouseEnabled;
		o.tickEnabled = this.tickEnabled;
		o.name = this.name;
		o.regX = this.regX;
		o.regY = this.regY;
		o.rotation = this.rotation;
		o.scaleX = this.scaleX;
		o.scaleY = this.scaleY;
		o.shadow = this.shadow;
		o.skewX = this.skewX;
		o.skewY = this.skewY;
		o.visible = this.visible;
		o.x  = this.x;
		o.y = this.y;
		o.compositeOperation = this.compositeOperation;
		o.snapToPixel = this.snapToPixel;
		o.filters = this.filters==null?null:this.filters.slice(0);
		o.mask = this.mask;
		o.hitArea = this.hitArea;
		o.cursor = this.cursor;
		o._bounds = this._bounds;
		return o;
	};

	/**
	 * @method _applyShadow
	 * @protected
	 * @param {CanvasRenderingContext2D} ctx
	 * @param {Shadow} shadow
	 **/
	p._applyShadow = function(ctx, shadow) {
		shadow = shadow || Shadow.identity;
		ctx.shadowColor = shadow.color;
		ctx.shadowOffsetX = shadow.offsetX;
		ctx.shadowOffsetY = shadow.offsetY;
		ctx.shadowBlur = shadow.blur;
	};
	
	
	/**
	 * @method _tick
	 * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
	 * @protected
	 **/
	p._tick = function(evtObj) {
		// because tick can be really performance sensitive, check for listeners before calling dispatchEvent.
		var ls = this._listeners;
		if (ls && ls["tick"]) {
			// reset & reuse the event object to avoid construction / GC costs:
			evtObj.target = null;
			evtObj.propagationStopped = evtObj.immediatePropagationStopped = false;
			this.dispatchEvent(evtObj);
		}
	};

	/**
	 * @method _testHit
	 * @protected
	 * @param {CanvasRenderingContext2D} ctx
	 * @return {Boolean}
	 **/
	p._testHit = function(ctx) {
		try {
			var hit = ctx.getImageData(0, 0, 1, 1).data[3] > 1;
		} catch (e) {
			if (!DisplayObject.suppressCrossDomainErrors) {
				throw "An error has occurred. This is most likely due to security restrictions on reading canvas pixel data with local or cross-domain images.";
			}
		}
		return hit;
	};

	/**
	 * @method _applyFilters
	 * @protected
	 **/
	p._applyFilters = function() {
		if (!this.filters || this.filters.length == 0 || !this.cacheCanvas) { return; }
		var l = this.filters.length;
		var ctx = this.cacheCanvas.getContext("2d");
		var w = this.cacheCanvas.width;
		var h = this.cacheCanvas.height;
		for (var i=0; i<l; i++) {
			this.filters[i].applyFilter(ctx, 0, 0, w, h);
		}
	};
	
	/**
	 * @method _getFilterBounds
	 * @return {Rectangle}
	 * @protected
	 **/
	p._getFilterBounds = function(rect) {
		var l, filters = this.filters, bounds = this._rectangle.setValues(0,0,0,0);
		if (!filters || !(l=filters.length)) { return bounds; }
		
		for (var i=0; i<l; i++) {
			var f = this.filters[i];
			f.getBounds&&f.getBounds(bounds);
		}
		return bounds;
	};
	
	/**
	 * @method _getBounds
	 * @param {Matrix2D} matrix
	 * @param {Boolean} ignoreTransform If true, does not apply this object's transform.
	 * @return {Rectangle}
	 * @protected
	 **/
	p._getBounds = function(matrix, ignoreTransform){
		return this._transformBounds(this.getBounds(), matrix, ignoreTransform);
	};
	
	/**
	 * @method _transformBounds
	 * @param {Rectangle} bounds
	 * @param {Matrix2D} matrix
	 * @param {Boolean} ignoreTransform
	 * @return {Rectangle}
	 * @protected
	 **/
	p._transformBounds = function(bounds, matrix, ignoreTransform) {
		if (!bounds) { return bounds; }
		var x = bounds.x, y = bounds.y, width = bounds.width, height = bounds.height, mtx = this._props.matrix;
		mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
		
		if (x || y) { mtx.appendTransform(0,0,1,1,0,0,0,-x,-y); } // TODO: simplify this.
		if (matrix) { mtx.prependMatrix(matrix); }
		
		var x_a = width*mtx.a, x_b = width*mtx.b;
		var y_c = height*mtx.c, y_d = height*mtx.d;
		var tx = mtx.tx, ty = mtx.ty;
		
		var minX = tx, maxX = tx, minY = ty, maxY = ty;

		if ((x = x_a + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
		if ((x = x_a + y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
		if ((x = y_c + tx) < minX) { minX = x; } else if (x > maxX) { maxX = x; }
		
		if ((y = x_b + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
		if ((y = x_b + y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
		if ((y = y_d + ty) < minY) { minY = y; } else if (y > maxY) { maxY = y; }
		
		return bounds.setValues(minX, minY, maxX-minX, maxY-minY);
	};
	
	/**
	 * Indicates whether the display object has any mouse event listeners or a cursor.
	 * @method _isMouseOpaque
	 * @return {Boolean}
	 * @protected
	 **/
	p._hasMouseEventListener = function() {
		var evts = DisplayObject._MOUSE_EVENTS;
		for (var i= 0, l=evts.length; i<l; i++) {
			if (this.hasEventListener(evts[i])) { return true; }
		}
		return !!this.cursor;
	};

	createjs.DisplayObject = createjs.promote(DisplayObject, "EventDispatcher");
}());

//##############################################################################
// Container.js
//##############################################################################

(function() {
	"use strict";
	

// constructor:
/**
 * A Container is a nestable display list that allows you to work with compound display elements. For  example you could
 * group arm, leg, torso and head {{#crossLink "Bitmap"}}{{/crossLink}} instances together into a Person Container, and
 * transform them as a group, while still being able to move the individual parts relative to each other. Children of
 * containers have their <code>transform</code> and <code>alpha</code> properties concatenated with their parent
 * Container.
 *
 * For example, a {{#crossLink "Shape"}}{{/crossLink}} with x=100 and alpha=0.5, placed in a Container with <code>x=50</code>
 * and <code>alpha=0.7</code> will be rendered to the canvas at <code>x=150</code> and <code>alpha=0.35</code>.
 * Containers have some overhead, so you generally shouldn't create a Container to hold a single child.
 *
 * <h4>Example</h4>
 *
 *      var container = new createjs.Container();
 *      container.addChild(bitmapInstance, shapeInstance);
 *      container.x = 100;
 *
 * @class Container
 * @extends DisplayObject
 * @constructor
 **/
	function Container() {
		this.DisplayObject_constructor();
		
	// public properties:
		/**
		 * The array of children in the display list. You should usually use the child management methods such as
		 * {{#crossLink "Container/addChild"}}{{/crossLink}}, {{#crossLink "Container/removeChild"}}{{/crossLink}},
		 * {{#crossLink "Container/swapChildren"}}{{/crossLink}}, etc, rather than accessing this directly, but it is
		 * included for advanced uses.
		 * @property children
		 * @type Array
		 * @default null
		 **/
		this.children = [];
		
		/**
		 * Indicates whether the children of this container are independently enabled for mouse/pointer interaction.
		 * If false, the children will be aggregated under the container - for example, a click on a child shape would
		 * trigger a click event on the container.
		 * @property mouseChildren
		 * @type Boolean
		 * @default true
		 **/
		this.mouseChildren = true;
		
		/**
		 * If false, the tick will not be propagated to children of this Container. This can provide some performance benefits.
		 * In addition to preventing the "tick" event from being dispatched, it will also prevent tick related updates
		 * on some display objects (ex. Sprite & MovieClip frame advancing, DOMElement visibility handling).
		 * @property tickChildren
		 * @type Boolean
		 * @default true
		 **/
		this.tickChildren = true;
	}
	var p = createjs.extend(Container, createjs.DisplayObject);
	
	
// getter / setters:
	/**
	 * Use the {{#crossLink "Container/numChildren:property"}}{{/crossLink}} property instead.
	 * @method getNumChildren
	 * @return {Number}
	 * @deprecated
	 **/
	p.getNumChildren = function() {
		return this.children.length;
	};

	/**
	 * Returns the number of children in the container.
	 * @property numChildren
	 * @type {Number}
	 * @readonly
	 **/
	try {
		Object.defineProperties(p, {
			numChildren: { get: p.getNumChildren }
		});
	} catch (e) {}
	

// public methods:
	/**
	 * Constructor alias for backwards compatibility. This method will be removed in future versions.
	 * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
	 * @method initialize
	 * @deprecated in favour of `createjs.promote()`
	 **/
	p.initialize = Container; // TODO: deprecated.
	
	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || this.children.length;
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
		
		// this ensures we don't have issues with display list changes that occur during a draw:
		var list = this.children.slice();
		for (var i=0,l=list.length; i<l; i++) {
			var child = list[i];
			if (!child.isVisible()) { continue; }
			
			// draw the child:
			ctx.save();
			child.updateContext(ctx);
			child.draw(ctx);
			ctx.restore();
		}
		return true;
	};
	
	/**
	 * Adds a child to the top of the display list.
	 *
	 * <h4>Example</h4>
	 *
	 * 		container.addChild(bitmapInstance);
	 *
	 * You can also add multiple children at once:
	 *
	 * 		container.addChild(bitmapInstance, shapeInstance, textInstance);
	 *
	 * @method addChild
	 * @param {DisplayObject} child The display object to add.
	 * @return {DisplayObject} The child that was added, or the last child if multiple children were added.
	 **/
	p.addChild = function(child) {
		if (child == null) { return child; }
		var l = arguments.length;
		if (l > 1) {
			for (var i=0; i<l; i++) { this.addChild(arguments[i]); }
			return arguments[l-1];
		}
		if (child.parent) { child.parent.removeChild(child); }
		child.parent = this;
		this.children.push(child);
		child.dispatchEvent("added");
		return child;
	};

	/**
	 * Adds a child to the display list at the specified index, bumping children at equal or greater indexes up one, and
	 * setting its parent to this Container.
	 *
	 * <h4>Example</h4>
	 *
	 *      addChildAt(child1, index);
	 *
	 * You can also add multiple children, such as:
	 *
	 *      addChildAt(child1, child2, ..., index);
	 *
	 * The index must be between 0 and numChildren. For example, to add myShape under otherShape in the display list,
	 * you could use:
	 *
	 *      container.addChildAt(myShape, container.getChildIndex(otherShape));
	 *
	 * This would also bump otherShape's index up by one. Fails silently if the index is out of range.
	 *
	 * @method addChildAt
	 * @param {DisplayObject} child The display object to add.
	 * @param {Number} index The index to add the child at.
	 * @return {DisplayObject} Returns the last child that was added, or the last child if multiple children were added.
	 **/
	p.addChildAt = function(child, index) {
		var l = arguments.length;
		var indx = arguments[l-1]; // can't use the same name as the index param or it replaces arguments[1]
		if (indx < 0 || indx > this.children.length) { return arguments[l-2]; }
		if (l > 2) {
			for (var i=0; i<l-1; i++) { this.addChildAt(arguments[i], indx+i); }
			return arguments[l-2];
		}
		if (child.parent) { child.parent.removeChild(child); }
		child.parent = this;
		this.children.splice(index, 0, child);
		child.dispatchEvent("added");
		return child;
	};

	/**
	 * Removes the specified child from the display list. Note that it is faster to use removeChildAt() if the index is
	 * already known.
	 *
	 * <h4>Example</h4>
	 *
	 *      container.removeChild(child);
	 *
	 * You can also remove multiple children:
	 *
	 *      removeChild(child1, child2, ...);
	 *
	 * Returns true if the child (or children) was removed, or false if it was not in the display list.
	 * @method removeChild
	 * @param {DisplayObject} child The child to remove.
	 * @return {Boolean} true if the child (or children) was removed, or false if it was not in the display list.
	 **/
	p.removeChild = function(child) {
		var l = arguments.length;
		if (l > 1) {
			var good = true;
			for (var i=0; i<l; i++) { good = good && this.removeChild(arguments[i]); }
			return good;
		}
		return this.removeChildAt(createjs.indexOf(this.children, child));
	};

	/**
	 * Removes the child at the specified index from the display list, and sets its parent to null.
	 *
	 * <h4>Example</h4>
	 *
	 *      container.removeChildAt(2);
	 *
	 * You can also remove multiple children:
	 *
	 *      container.removeChild(2, 7, ...)
	 *
	 * Returns true if the child (or children) was removed, or false if any index was out of range.
	 * @method removeChildAt
	 * @param {Number} index The index of the child to remove.
	 * @return {Boolean} true if the child (or children) was removed, or false if any index was out of range.
	 **/
	p.removeChildAt = function(index) {
		var l = arguments.length;
		if (l > 1) {
			var a = [];
			for (var i=0; i<l; i++) { a[i] = arguments[i]; }
			a.sort(function(a, b) { return b-a; });
			var good = true;
			for (var i=0; i<l; i++) { good = good && this.removeChildAt(a[i]); }
			return good;
		}
		if (index < 0 || index > this.children.length-1) { return false; }
		var child = this.children[index];
		if (child) { child.parent = null; }
		this.children.splice(index, 1);
		child.dispatchEvent("removed");
		return true;
	};

	/**
	 * Removes all children from the display list.
	 *
	 * <h4>Example</h4>
	 *
	 * 	container.removeAllChildren();
	 *
	 * @method removeAllChildren
	 **/
	p.removeAllChildren = function() {
		var kids = this.children;
		while (kids.length) { this.removeChildAt(0); }
	};

	/**
	 * Returns the child at the specified index.
	 *
	 * <h4>Example</h4>
	 *
	 *      container.getChildAt(2);
	 *
	 * @method getChildAt
	 * @param {Number} index The index of the child to return.
	 * @return {DisplayObject} The child at the specified index. Returns null if there is no child at the index.
	 **/
	p.getChildAt = function(index) {
		return this.children[index];
	};
	
	/**
	 * Returns the child with the specified name.
	 * @method getChildByName
	 * @param {String} name The name of the child to return.
	 * @return {DisplayObject} The child with the specified name.
	 **/
	p.getChildByName = function(name) {
		var kids = this.children;
		for (var i=0,l=kids.length;i<l;i++) {
			if(kids[i].name == name) { return kids[i]; }
		}
		return null;
	};

	/**
	 * Performs an array sort operation on the child list.
	 *
	 * <h4>Example: Display children with a higher y in front.</h4>
	 * 
	 *      var sortFunction = function(obj1, obj2, options) {
	 *          if (obj1.y > obj2.y) { return 1; }
	 *          if (obj1.y < obj2.y) { return -1; }
	 *          return 0;
	 *      }
	 *      container.sortChildren(sortFunction);
	 *
	 * @method sortChildren
	 * @param {Function} sortFunction the function to use to sort the child list. See JavaScript's <code>Array.sort</code>
	 * documentation for details.
	 **/
	p.sortChildren = function(sortFunction) {
		this.children.sort(sortFunction);
	};

	/**
	 * Returns the index of the specified child in the display list, or -1 if it is not in the display list.
	 *
	 * <h4>Example</h4>
	 *
	 *      var index = container.getChildIndex(child);
	 *
	 * @method getChildIndex
	 * @param {DisplayObject} child The child to return the index of.
	 * @return {Number} The index of the specified child. -1 if the child is not found.
	 **/
	p.getChildIndex = function(child) {
		return createjs.indexOf(this.children, child);
	};
	
	/**
	 * Swaps the children at the specified indexes. Fails silently if either index is out of range.
	 * @method swapChildrenAt
	 * @param {Number} index1
	 * @param {Number} index2
	 **/
	p.swapChildrenAt = function(index1, index2) {
		var kids = this.children;
		var o1 = kids[index1];
		var o2 = kids[index2];
		if (!o1 || !o2) { return; }
		kids[index1] = o2;
		kids[index2] = o1;
	};
	
	/**
	 * Swaps the specified children's depth in the display list. Fails silently if either child is not a child of this
	 * Container.
	 * @method swapChildren
	 * @param {DisplayObject} child1
	 * @param {DisplayObject} child2
	 **/
	p.swapChildren = function(child1, child2) {
		var kids = this.children;
		var index1,index2;
		for (var i=0,l=kids.length;i<l;i++) {
			if (kids[i] == child1) { index1 = i; }
			if (kids[i] == child2) { index2 = i; }
			if (index1 != null && index2 != null) { break; }
		}
		if (i==l) { return; } // TODO: throw error?
		kids[index1] = child2;
		kids[index2] = child1;
	};
	
	/**
	 * Changes the depth of the specified child. Fails silently if the child is not a child of this container, or the index is out of range.
	 * @param {DisplayObject} child
	 * @param {Number} index  
	 * @method setChildIndex
	 **/
	p.setChildIndex = function(child, index) {
		var kids = this.children, l=kids.length;
		if (child.parent != this || index < 0 || index >= l) { return; }
		for (var i=0;i<l;i++) {
			if (kids[i] == child) { break; }
		}
		if (i==l || i == index) { return; }
		kids.splice(i,1);
		kids.splice(index,0,child);
	};

	/**
	 * Returns true if the specified display object either is this container or is a descendent (child, grandchild, etc)
	 * of this container.
	 * @method contains
	 * @param {DisplayObject} child The DisplayObject to be checked.
	 * @return {Boolean} true if the specified display object either is this container or is a descendent.
	 **/
	p.contains = function(child) {
		while (child) {
			if (child == this) { return true; }
			child = child.parent;
		}
		return false;
	};

	/**
	 * Tests whether the display object intersects the specified local point (ie. draws a pixel with alpha > 0 at the
	 * specified position). This ignores the alpha, shadow and compositeOperation of the display object, and all
	 * transform properties including regX/Y.
	 * @method hitTest
	 * @param {Number} x The x position to check in the display object's local coordinates.
	 * @param {Number} y The y position to check in the display object's local coordinates.
	 * @return {Boolean} A Boolean indicating whether there is a visible section of a DisplayObject that overlaps the specified
	 * coordinates.
	 **/
	p.hitTest = function(x, y) {
		// TODO: optimize to use the fast cache check where possible.
		return (this.getObjectUnderPoint(x, y) != null);
	};

	/**
	 * Returns an array of all display objects under the specified coordinates that are in this container's display
	 * list. This routine ignores any display objects with {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
	 * set to `false`. The array will be sorted in order of visual depth, with the top-most display object at index 0.
	 * This uses shape based hit detection, and can be an expensive operation to run, so it is best to use it carefully.
	 * For example, if testing for objects under the mouse, test on tick (instead of on {{#crossLink "DisplayObject/mousemove:event"}}{{/crossLink}}),
	 * and only if the mouse's position has changed.
	 * 
	 * <ul>
	 *     <li>By default (mode=0) this method evaluates all display objects.</li>
	 *     <li>By setting the `mode` parameter to `1`, the {{#crossLink "DisplayObject/mouseEnabled:property"}}{{/crossLink}}
	 * 		and {{#crossLink "mouseChildren:property"}}{{/crossLink}} properties will be respected.</li>
	 * 	   <li>Setting the `mode` to `2` additionally excludes display objects that do not have active mouse event
	 * 	   	listeners or a {{#crossLink "DisplayObject:cursor:property"}}{{/crossLink}} property. That is, only objects
	 * 	   	that would normally intercept mouse interaction will be included. This can significantly improve performance
	 * 	   	in some cases by reducing the number of display objects that need to be tested.</li>
	 * </li>
	 * 
	 * This method accounts for both {{#crossLink "DisplayObject/hitArea:property"}}{{/crossLink}} and {{#crossLink "DisplayObject/mask:property"}}{{/crossLink}}.
	 * @method getObjectsUnderPoint
	 * @param {Number} x The x position in the container to test.
	 * @param {Number} y The y position in the container to test.
	 * @param {Number} [mode=0] The mode to use to determine which display objects to include. 0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
	 * @return {Array} An Array of DisplayObjects under the specified coordinates.
	 **/
	p.getObjectsUnderPoint = function(x, y, mode) {
		var arr = [];
		var pt = this.localToGlobal(x, y);
		this._getObjectsUnderPoint(pt.x, pt.y, arr, mode>0, mode==1);
		return arr;
	};

	/**
	 * Similar to {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}}, but returns only the top-most display
	 * object. This runs significantly faster than <code>getObjectsUnderPoint()</code>, but is still potentially an expensive
	 * operation. See {{#crossLink "Container/getObjectsUnderPoint"}}{{/crossLink}} for more information.
	 * @method getObjectUnderPoint
	 * @param {Number} x The x position in the container to test.
	 * @param {Number} y The y position in the container to test.
	 * @param {Number} mode The mode to use to determine which display objects to include.  0-all, 1-respect mouseEnabled/mouseChildren, 2-only mouse opaque objects.
	 * @return {DisplayObject} The top-most display object under the specified coordinates.
	 **/
	p.getObjectUnderPoint = function(x, y, mode) {
		var pt = this.localToGlobal(x, y);
		return this._getObjectsUnderPoint(pt.x, pt.y, null, mode>0, mode==1);
	};
	
	/**
	 * Docced in superclass.
	 */
	p.getBounds = function() {
		return this._getBounds(null, true);
	};
	
	
	/**
	 * Docced in superclass.
	 */
	p.getTransformedBounds = function() {
		return this._getBounds();
	};

	/**
	 * Returns a clone of this Container. Some properties that are specific to this instance's current context are
	 * reverted to their defaults (for example .parent).
	 * @method clone
	 * @param {Boolean} [recursive=false] If true, all of the descendants of this container will be cloned recursively. If false, the
	 * properties of the container will be cloned, but the new instance will not have any children.
	 * @return {Container} A clone of the current Container instance.
	 **/
	p.clone = function(recursive) {
		var o = this._cloneProps(new Container());
		if (recursive) { this._cloneChildren(o); }
		return o;
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Container (name="+  this.name +")]";
	};


// private methods:
	/**
	 * @method _tick
	 * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
	 * @protected
	 **/
	p._tick = function(evtObj) {
		if (this.tickChildren) {
			for (var i=this.children.length-1; i>=0; i--) {
				var child = this.children[i];
				if (child.tickEnabled && child._tick) { child._tick(evtObj); }
			}
		}
		this.DisplayObject__tick(evtObj);
	};
	
	/**
	 * Recursively clones all children of this container, and adds them to the target container.
	 * @method cloneChildren
	 * @protected
	 * @param {Container} o The target container.
	 **/
	p._cloneChildren = function(o) {
		if (o.children.length) { o.removeAllChildren(); }
		var arr = o.children;
		for (var i=0, l=this.children.length; i<l; i++) {
			var clone = this.children[i].clone(true);
			clone.parent = o;
			arr.push(clone);
		}
	};

	/**
	 * @method _getObjectsUnderPoint
	 * @param {Number} x
	 * @param {Number} y
	 * @param {Array} arr
	 * @param {Boolean} mouse If true, it will respect mouse interaction properties like mouseEnabled, mouseChildren, and active listeners.
	 * @param {Boolean} activeListener If true, there is an active mouse event listener on a parent object.
	 * @param {Number} currentDepth Indicates the current depth of the search.
	 * @return {DisplayObject}
	 * @protected
	 **/
	p._getObjectsUnderPoint = function(x, y, arr, mouse, activeListener, currentDepth) {
		currentDepth = currentDepth || 0;
		if (!currentDepth && !this._testMask(this, x, y)) { return null; }
		var mtx, ctx = createjs.DisplayObject._hitTestContext;
		activeListener = activeListener || (mouse&&this._hasMouseEventListener());

		// draw children one at a time, and check if we get a hit:
		var children = this.children, l = children.length;
		for (var i=l-1; i>=0; i--) {
			var child = children[i];
			var hitArea = child.hitArea;
			if (!child.visible || (!hitArea && !child.isVisible()) || (mouse && !child.mouseEnabled)) { continue; }
			if (!hitArea && !this._testMask(child, x, y)) { continue; }
			
			// if a child container has a hitArea then we only need to check its hitArea, so we can treat it as a normal DO:
			if (!hitArea && child instanceof Container) {
				var result = child._getObjectsUnderPoint(x, y, arr, mouse, activeListener, currentDepth+1);
				if (!arr && result) { return (mouse && !this.mouseChildren) ? this : result; }
			} else {
				if (mouse && !activeListener && !child._hasMouseEventListener()) { continue; }
				
				// TODO: can we pass displayProps forward, to avoid having to calculate this backwards every time? It's kind of a mixed bag. When we're only hunting for DOs with event listeners, it may not make sense.
				var props = child.getConcatenatedDisplayProps(child._props);
				mtx = props.matrix;
				
				if (hitArea) {
					mtx.appendMatrix(hitArea.getMatrix(hitArea._props.matrix));
					props.alpha = hitArea.alpha;
				}
				
				ctx.globalAlpha = props.alpha;
				ctx.setTransform(mtx.a,  mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
				(hitArea||child).draw(ctx);
				if (!this._testHit(ctx)) { continue; }
				ctx.setTransform(1, 0, 0, 1, 0, 0);
				ctx.clearRect(0, 0, 2, 2);
				if (arr) { arr.push(child); }
				else { return (mouse && !this.mouseChildren) ? this : child; }
			}
		}
		return null;
	};
	
	/**
	 * @method _testMask
	 * @param {DisplayObject} target
	 * @param {Number} x
	 * @param {Number} y
	 * @return {Boolean} Indicates whether the x/y is within the masked region.
	 * @protected
	 **/
	p._testMask = function(target, x, y) {
		var mask = target.mask;
		if (!mask || !mask.graphics || mask.graphics.isEmpty()) { return true; }
		
		var mtx = this._props.matrix, parent = target.parent;
		mtx = parent ? parent.getConcatenatedMatrix(mtx) : mtx.identity();
		mtx = mask.getMatrix(mask._props.matrix).prependMatrix(mtx);
		
		var ctx = createjs.DisplayObject._hitTestContext;
		ctx.setTransform(mtx.a,  mtx.b, mtx.c, mtx.d, mtx.tx-x, mtx.ty-y);
		
		// draw the mask as a solid fill:
		mask.graphics.drawAsPath(ctx);
		ctx.fillStyle = "#000";
		ctx.fill();
		
		if (!this._testHit(ctx)) { return false; }
		ctx.setTransform(1, 0, 0, 1, 0, 0);
		ctx.clearRect(0, 0, 2, 2);
		
		return true;
	};
	
	/**
	 * @method _getBounds
	 * @param {Matrix2D} matrix
	 * @param {Boolean} ignoreTransform If true, does not apply this object's transform.
	 * @return {Rectangle}
	 * @protected
	 **/
	p._getBounds = function(matrix, ignoreTransform) {
		var bounds = this.DisplayObject_getBounds();
		if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
		
		var mtx = this._props.matrix;
		mtx = ignoreTransform ? mtx.identity() : this.getMatrix(mtx);
		if (matrix) { mtx.prependMatrix(matrix); }
		
		var l = this.children.length, rect=null;
		for (var i=0; i<l; i++) {
			var child = this.children[i];
			if (!child.visible || !(bounds = child._getBounds(mtx))) { continue; }
			if (rect) { rect.extend(bounds.x, bounds.y, bounds.width, bounds.height); }
			else { rect = bounds.clone(); }
		}
		return rect;
	};


	createjs.Container = createjs.promote(Container, "DisplayObject");
}());

//##############################################################################
// Stage.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * A stage is the root level {{#crossLink "Container"}}{{/crossLink}} for a display list. Each time its {{#crossLink "Stage/tick"}}{{/crossLink}}
	 * method is called, it will render its display list to its target canvas.
	 *
	 * <h4>Example</h4>
	 * This example creates a stage, adds a child to it, then uses {{#crossLink "Ticker"}}{{/crossLink}} to update the child
	 * and redraw the stage using {{#crossLink "Stage/update"}}{{/crossLink}}.
	 *
	 *      var stage = new createjs.Stage("canvasElementId");
	 *      var image = new createjs.Bitmap("imagePath.png");
	 *      stage.addChild(image);
	 *      createjs.Ticker.addEventListener("tick", handleTick);
	 *      function handleTick(event) {
	 *          image.x += 10;
	 *          stage.update();
	 *      }
	 *
	 * @class Stage
	 * @extends Container
	 * @constructor
	 * @param {HTMLCanvasElement | String | Object} canvas A canvas object that the Stage will render to, or the string id
	 * of a canvas object in the current document.
	 **/
	function Stage(canvas) {
		this.Container_constructor();
	
	
	// public properties:
		/**
		 * Indicates whether the stage should automatically clear the canvas before each render. You can set this to <code>false</code>
		 * to manually control clearing (for generative art, or when pointing multiple stages at the same canvas for
		 * example).
		 *
		 * <h4>Example</h4>
		 *
		 *      var stage = new createjs.Stage("canvasId");
		 *      stage.autoClear = false;
		 *
		 * @property autoClear
		 * @type Boolean
		 * @default true
		 **/
		this.autoClear = true;
	
		/**
		 * The canvas the stage will render to. Multiple stages can share a single canvas, but you must disable autoClear for all but the
		 * first stage that will be ticked (or they will clear each other's render).
		 *
		 * When changing the canvas property you must disable the events on the old canvas, and enable events on the
		 * new canvas or mouse events will not work as expected. For example:
		 *
		 *      myStage.enableDOMEvents(false);
		 *      myStage.canvas = anotherCanvas;
		 *      myStage.enableDOMEvents(true);
		 *
		 * @property canvas
		 * @type HTMLCanvasElement | Object
		 **/
		this.canvas = (typeof canvas == "string") ? document.getElementById(canvas) : canvas;
	
		/**
		 * The current mouse X position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
		 * position over the canvas, and mouseInBounds will be set to false.
		 * @property mouseX
		 * @type Number
		 * @readonly
		 **/
		this.mouseX = 0;
	
		/**
		 * The current mouse Y position on the canvas. If the mouse leaves the canvas, this will indicate the most recent
		 * position over the canvas, and mouseInBounds will be set to false.
		 * @property mouseY
		 * @type Number
		 * @readonly
		 **/
		this.mouseY = 0;
	
		/**
		 * Specifies the area of the stage to affect when calling update. This can be use to selectively
		 * re-draw specific regions of the canvas. If null, the whole canvas area is drawn.
		 * @property drawRect
		 * @type {Rectangle}
		 */
		this.drawRect = null;
	
		/**
		 * Indicates whether display objects should be rendered on whole pixels. You can set the
		 * {{#crossLink "DisplayObject/snapToPixel"}}{{/crossLink}} property of
		 * display objects to false to enable/disable this behaviour on a per instance basis.
		 * @property snapToPixelEnabled
		 * @type Boolean
		 * @default false
		 **/
		this.snapToPixelEnabled = false;
	
		/**
		 * Indicates whether the mouse is currently within the bounds of the canvas.
		 * @property mouseInBounds
		 * @type Boolean
		 * @default false
		 **/
		this.mouseInBounds = false;
	
		/**
		 * If true, tick callbacks will be called on all display objects on the stage prior to rendering to the canvas.
		 * @property tickOnUpdate
		 * @type Boolean
		 * @default true
		 **/
		this.tickOnUpdate = true;
	
		/**
		 * If true, mouse move events will continue to be called when the mouse leaves the target canvas. See
		 * {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}}, and {{#crossLink "MouseEvent"}}{{/crossLink}}
		 * x/y/rawX/rawY.
		 * @property mouseMoveOutside
		 * @type Boolean
		 * @default false
		 **/
		this.mouseMoveOutside = false;
		
		
		/**
		 * Prevents selection of other elements in the html page if the user clicks and drags, or double clicks on the canvas.
		 * This works by calling `preventDefault()` on any mousedown events (or touch equivalent) originating on the canvas.
		 * @property preventSelection
		 * @type Boolean
		 * @default true
		 **/
		this.preventSelection = true;
	
		/**
		 * The hitArea property is not supported for Stage.
		 * @property hitArea
		 * @type {DisplayObject}
		 * @default null
		 */
		 
		 
	// private properties:
		/**
		 * Holds objects with data for each active pointer id. Each object has the following properties:
		 * x, y, event, target, overTarget, overX, overY, inBounds, posEvtObj (native event that last updated position)
		 * @property _pointerData
		 * @type {Object}
		 * @private
		 */
		this._pointerData = {};
	
		/**
		 * Number of active pointers.
		 * @property _pointerCount
		 * @type {Object}
		 * @private
		 */
		this._pointerCount = 0;
	
		/**
		 * The ID of the primary pointer.
		 * @property _primaryPointerID
		 * @type {Object}
		 * @private
		 */
		this._primaryPointerID = null;
	
		/**
		 * @property _mouseOverIntervalID
		 * @protected
		 * @type Number
		 **/
		this._mouseOverIntervalID = null;
		
		/**
		 * @property _nextStage
		 * @protected
		 * @type Stage
		 **/
		this._nextStage = null;
		
		/**
		 * @property _prevStage
		 * @protected
		 * @type Stage
		 **/
		this._prevStage = null;
		
		
	// initialize:
		this.enableDOMEvents(true);
	}
	var p = createjs.extend(Stage, createjs.Container);

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// events:
	/**
	 * Dispatched when the user moves the mouse over the canvas.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event stagemousemove
	 * @since 0.6.0
	 */

	/**
	 * Dispatched when the user presses their left mouse button on the canvas. See the {{#crossLink "MouseEvent"}}{{/crossLink}}
	 * class for a listing of event properties.
	 * @event stagemousedown
	 * @since 0.6.0
	 */

	/**
	 * Dispatched when the user the user presses somewhere on the stage, then releases the mouse button anywhere that the page can detect it (this varies slightly between browsers).
	 * You can use {{#crossLink "Stage/mouseInBounds:property"}}{{/crossLink}} to check whether the mouse is currently within the stage bounds.
	 * See the {{#crossLink "MouseEvent"}}{{/crossLink}} class for a listing of event properties.
	 * @event stagemouseup
	 * @since 0.6.0
	 */

	/**
	 * Dispatched when the mouse moves from within the canvas area (mouseInBounds == true) to outside it (mouseInBounds == false).
	 * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
	 * class for a listing of event properties.
	 * @event mouseleave
	 * @since 0.7.0
	 */

	/**
	 * Dispatched when the mouse moves into the canvas area (mouseInBounds == false) from outside it (mouseInBounds == true).
	 * This is currently only dispatched for mouse input (not touch). See the {{#crossLink "MouseEvent"}}{{/crossLink}}
	 * class for a listing of event properties.
	 * @event mouseenter
	 * @since 0.7.0
	 */
	 
	/**
	 * Dispatched each update immediately before the tick event is propagated through the display list.
	 * You can call preventDefault on the event object to cancel propagating the tick event.
	 * @event tickstart
	 * @since 0.7.0
	 */
	 
	/**
	 * Dispatched each update immediately after the tick event is propagated through the display list. Does not fire if
	 * tickOnUpdate is false. Precedes the "drawstart" event.
	 * @event tickend
	 * @since 0.7.0
	 */
	 
	/**
	 * Dispatched each update immediately before the canvas is cleared and the display list is drawn to it.
	 * You can call preventDefault on the event object to cancel the draw.
	 * @event drawstart
	 * @since 0.7.0
	 */
	 
	/**
	 * Dispatched each update immediately after the display list is drawn to the canvas and the canvas context is restored.
	 * @event drawend
	 * @since 0.7.0
	 */

	 
// getter / setters:
	/**
	 * Specifies a target stage that will have mouse / touch interactions relayed to it after this stage handles them.
	 * This can be useful in cases where you have multiple layered canvases and want user interactions
	 * events to pass through. For example, this would relay mouse events from topStage to bottomStage:
	 *
	 *      topStage.nextStage = bottomStage;
	 *
	 * To disable relaying, set nextStage to null.
	 * 
	 * MouseOver, MouseOut, RollOver, and RollOut interactions are also passed through using the mouse over settings
	 * of the top-most stage, but are only processed if the target stage has mouse over interactions enabled.
	 * Considerations when using roll over in relay targets:<OL>
	 * <LI> The top-most (first) stage must have mouse over interactions enabled (via enableMouseOver)</LI>
	 * <LI> All stages that wish to participate in mouse over interaction must enable them via enableMouseOver</LI>
	 * <LI> All relay targets will share the frequency value of the top-most stage</LI>
	 * </OL>
	 * To illustrate, in this example the targetStage would process mouse over interactions at 10hz (despite passing
	 * 30 as it's desired frequency):
	 * 	topStage.nextStage = targetStage;
	 * 	topStage.enableMouseOver(10);
	 * 	targetStage.enableMouseOver(30);
	 * 
	 * If the target stage's canvas is completely covered by this stage's canvas, you may also want to disable its
	 * DOM events using:
	 * 
	 *	targetStage.enableDOMEvents(false);
	 * 
	 * @property nextStage
	 * @type {Stage}
	 **/
	p._get_nextStage = function() {
		return this._nextStage;
	};
	p._set_nextStage = function(value) {
		if (this._nextStage) { this._nextStage._prevStage = null; }
		if (value) { value._prevStage = this; }
		this._nextStage = value;
	};
	
	try {
		Object.defineProperties(p, {
			nextStage: { get: p._get_nextStage, set: p._set_nextStage }
		});
	} catch (e) {} // TODO: use Log


// public methods:
	/**
	 * Each time the update method is called, the stage will call {{#crossLink "Stage/tick"}}{{/crossLink}}
	 * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false,
	 * and then render the display list to the canvas.
	 *
	 * @method update
	 * @param {Object} [props] Props object to pass to `tick()`. Should usually be a {{#crossLink "Ticker"}}{{/crossLink}} event object, or similar object with a delta property.
	 **/
	p.update = function(props) {
		if (!this.canvas) { return; }
		if (this.tickOnUpdate) { this.tick(props); }
		if (this.dispatchEvent("drawstart", false, true) === false) { return; }
		createjs.DisplayObject._snapToPixelEnabled = this.snapToPixelEnabled;
		var r = this.drawRect, ctx = this.canvas.getContext("2d");
		ctx.setTransform(1, 0, 0, 1, 0, 0);
		if (this.autoClear) {
			if (r) { ctx.clearRect(r.x, r.y, r.width, r.height); }
			else { ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1); }
		}
		ctx.save();
		if (this.drawRect) {
			ctx.beginPath();
			ctx.rect(r.x, r.y, r.width, r.height);
			ctx.clip();
		}
		this.updateContext(ctx);
		this.draw(ctx, false);
		ctx.restore();
		this.dispatchEvent("drawend");
	};
	
	/**
	 * Propagates a tick event through the display list. This is automatically called by {{#crossLink "Stage/update"}}{{/crossLink}}
	 * unless {{#crossLink "Stage/tickOnUpdate:property"}}{{/crossLink}} is set to false.
	 *
	 * If a props object is passed to `tick()`, then all of its properties will be copied to the event object that is
	 * propagated to listeners.
	 *
	 * Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
	 * a {{#crossLink "Ticker/tick:event"}}{{/crossLink}} event object (or equivalent object with a delta property) be
	 * passed as the `props` parameter to `tick()`. For example:
	 *
	 * 	Ticker.on("tick", handleTick);
	 * 	function handleTick(evtObj) {
	 * 		// clone the event object from Ticker, and add some custom data to it:
	 * 		var evt = evtObj.clone().set({greeting:"hello", name:"world"});
	 * 		
	 * 		// pass it to stage.update():
	 * 		myStage.update(evt); // subsequently calls tick() with the same param
	 * 	}
	 * 	
	 * 	// ...
	 * 	myDisplayObject.on("tick", handleDisplayObjectTick);
	 * 	function handleDisplayObjectTick(evt) {
	 * 		console.log(evt.delta); // the delta property from the Ticker tick event object
	 * 		console.log(evt.greeting, evt.name); // custom data: "hello world"
	 * 	}
	 * 
	 * @method tick
	 * @param {Object} [props] An object with properties that should be copied to the event object. Should usually be a Ticker event object, or similar object with a delta property.
	 **/
	p.tick = function(props) {
		if (!this.tickEnabled || this.dispatchEvent("tickstart", false, true) === false) { return; }
		var evtObj = new createjs.Event("tick");
		if (props) {
			for (var n in props) {
				if (props.hasOwnProperty(n)) { evtObj[n] = props[n]; }
			}
		}
		this._tick(evtObj);
		this.dispatchEvent("tickend");
	};

	/**
	 * Default event handler that calls the Stage {{#crossLink "Stage/update"}}{{/crossLink}} method when a {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}}
	 * event is received. This allows you to register a Stage instance as a event listener on {{#crossLink "Ticker"}}{{/crossLink}}
	 * directly, using:
	 *
	 *      Ticker.addEventListener("tick", myStage");
	 *
	 * Note that if you subscribe to ticks using this pattern, then the tick event object will be passed through to
	 * display object tick handlers, instead of <code>delta</code> and <code>paused</code> parameters.
	 * @property handleEvent
	 * @type Function
	 **/
	p.handleEvent = function(evt) {
		if (evt.type == "tick") { this.update(evt); }
	};

	/**
	 * Clears the target canvas. Useful if {{#crossLink "Stage/autoClear:property"}}{{/crossLink}} is set to `false`.
	 * @method clear
	 **/
	p.clear = function() {
		if (!this.canvas) { return; }
		var ctx = this.canvas.getContext("2d");
		ctx.setTransform(1, 0, 0, 1, 0, 0);
		ctx.clearRect(0, 0, this.canvas.width+1, this.canvas.height+1);
	};

	/**
	 * Returns a data url that contains a Base64-encoded image of the contents of the stage. The returned data url can
	 * be specified as the src value of an image element.
	 * @method toDataURL
	 * @param {String} [backgroundColor] The background color to be used for the generated image. Any valid CSS color
	 * value is allowed. The default value is a transparent background.
	 * @param {String} [mimeType="image/png"] The MIME type of the image format to be create. The default is "image/png". If an unknown MIME type
	 * is passed in, or if the browser does not support the specified MIME type, the default value will be used.
	 * @return {String} a Base64 encoded image.
	 **/
	p.toDataURL = function(backgroundColor, mimeType) {
		var data, ctx = this.canvas.getContext('2d'), w = this.canvas.width, h = this.canvas.height;

		if (backgroundColor) {
			data = ctx.getImageData(0, 0, w, h);
			var compositeOperation = ctx.globalCompositeOperation;
			ctx.globalCompositeOperation = "destination-over";
			
			ctx.fillStyle = backgroundColor;
			ctx.fillRect(0, 0, w, h);
		}

		var dataURL = this.canvas.toDataURL(mimeType||"image/png");

		if(backgroundColor) {
			ctx.putImageData(data, 0, 0);
			ctx.globalCompositeOperation = compositeOperation;
		}

		return dataURL;
	};

	/**
	 * Enables or disables (by passing a frequency of 0) mouse over ({{#crossLink "DisplayObject/mouseover:event"}}{{/crossLink}}
	 * and {{#crossLink "DisplayObject/mouseout:event"}}{{/crossLink}}) and roll over events ({{#crossLink "DisplayObject/rollover:event"}}{{/crossLink}}
	 * and {{#crossLink "DisplayObject/rollout:event"}}{{/crossLink}}) for this stage's display list. These events can
	 * be expensive to generate, so they are disabled by default. The frequency of the events can be controlled
	 * independently of mouse move events via the optional `frequency` parameter.
	 *
	 * <h4>Example</h4>
	 *
	 *      var stage = new createjs.Stage("canvasId");
	 *      stage.enableMouseOver(10); // 10 updates per second
	 *
	 * @method enableMouseOver
	 * @param {Number} [frequency=20] Optional param specifying the maximum number of times per second to broadcast
	 * mouse over/out events. Set to 0 to disable mouse over events completely. Maximum is 50. A lower frequency is less
	 * responsive, but uses less CPU.
	 **/
	p.enableMouseOver = function(frequency) {
		if (this._mouseOverIntervalID) {
			clearInterval(this._mouseOverIntervalID);
			this._mouseOverIntervalID = null;
			if (frequency == 0) {
				this._testMouseOver(true);
			}
		}
		if (frequency == null) { frequency = 20; }
		else if (frequency <= 0) { return; }
		var o = this;
		this._mouseOverIntervalID = setInterval(function(){ o._testMouseOver(); }, 1000/Math.min(50,frequency));
	};

	/**
	 * Enables or disables the event listeners that stage adds to DOM elements (window, document and canvas). It is good
	 * practice to disable events when disposing of a Stage instance, otherwise the stage will continue to receive
	 * events from the page.
	 *
	 * When changing the canvas property you must disable the events on the old canvas, and enable events on the
	 * new canvas or mouse events will not work as expected. For example:
	 *
	 *      myStage.enableDOMEvents(false);
	 *      myStage.canvas = anotherCanvas;
	 *      myStage.enableDOMEvents(true);
	 *
	 * @method enableDOMEvents
	 * @param {Boolean} [enable=true] Indicates whether to enable or disable the events. Default is true.
	 **/
	p.enableDOMEvents = function(enable) {
		if (enable == null) { enable = true; }
		var n, o, ls = this._eventListeners;
		if (!enable && ls) {
			for (n in ls) {
				o = ls[n];
				o.t.removeEventListener(n, o.f, false);
			}
			this._eventListeners = null;
		} else if (enable && !ls && this.canvas) {
			var t = window.addEventListener ? window : document;
			var _this = this;
			ls = this._eventListeners = {};
			ls["mouseup"] = {t:t, f:function(e) { _this._handleMouseUp(e)} };
			ls["mousemove"] = {t:t, f:function(e) { _this._handleMouseMove(e)} };
			ls["dblclick"] = {t:this.canvas, f:function(e) { _this._handleDoubleClick(e)} };
			ls["mousedown"] = {t:this.canvas, f:function(e) { _this._handleMouseDown(e)} };

			for (n in ls) {
				o = ls[n];
				o.t.addEventListener(n, o.f, false);
			}
		}
	};

	/**
	 * Stage instances cannot be cloned.
	 * @method clone
	 **/
	p.clone = function() {
		throw("Stage cannot be cloned.");
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Stage (name="+  this.name +")]";
	};


// private methods:
	/**
	 * @method _getElementRect
	 * @protected
	 * @param {HTMLElement} e
	 **/
	p._getElementRect = function(e) {
		var bounds;
		try { bounds = e.getBoundingClientRect(); } // this can fail on disconnected DOM elements in IE9
		catch (err) { bounds = {top: e.offsetTop, left: e.offsetLeft, width:e.offsetWidth, height:e.offsetHeight}; }

		var offX = (window.pageXOffset || document.scrollLeft || 0) - (document.clientLeft || document.body.clientLeft || 0);
		var offY = (window.pageYOffset || document.scrollTop || 0) - (document.clientTop  || document.body.clientTop  || 0);

		var styles = window.getComputedStyle ? getComputedStyle(e,null) : e.currentStyle; // IE <9 compatibility.
		var padL = parseInt(styles.paddingLeft)+parseInt(styles.borderLeftWidth);
		var padT = parseInt(styles.paddingTop)+parseInt(styles.borderTopWidth);
		var padR = parseInt(styles.paddingRight)+parseInt(styles.borderRightWidth);
		var padB = parseInt(styles.paddingBottom)+parseInt(styles.borderBottomWidth);

		// note: in some browsers bounds properties are read only.
		return {
			left: bounds.left+offX+padL,
			right: bounds.right+offX-padR,
			top: bounds.top+offY+padT,
			bottom: bounds.bottom+offY-padB
		}
	};

	/**
	 * @method _getPointerData
	 * @protected
	 * @param {Number} id
	 **/
	p._getPointerData = function(id) {
		var data = this._pointerData[id];
		if (!data) { data = this._pointerData[id] = {x:0,y:0}; }
		return data;
	};

	/**
	 * @method _handleMouseMove
	 * @protected
	 * @param {MouseEvent} e
	 **/
	p._handleMouseMove = function(e) {
		if(!e){ e = window.event; }
		this._handlePointerMove(-1, e, e.pageX, e.pageY);
	};

	/**
	 * @method _handlePointerMove
	 * @protected
	 * @param {Number} id
	 * @param {Event} e
	 * @param {Number} pageX
	 * @param {Number} pageY
	 * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
	 **/
	p._handlePointerMove = function(id, e, pageX, pageY, owner) {
		if (this._prevStage && owner === undefined) { return; } // redundant listener.
		if (!this.canvas) { return; }
		var nextStage=this._nextStage, o=this._getPointerData(id);

		var inBounds = o.inBounds;
		this._updatePointerPosition(id, e, pageX, pageY);
		if (inBounds || o.inBounds || this.mouseMoveOutside) {
			if (id === -1 && o.inBounds == !inBounds) {
				this._dispatchMouseEvent(this, (inBounds ? "mouseleave" : "mouseenter"), false, id, o, e);
			}
			
			this._dispatchMouseEvent(this, "stagemousemove", false, id, o, e);
			this._dispatchMouseEvent(o.target, "pressmove", true, id, o, e);
		}
		
		nextStage&&nextStage._handlePointerMove(id, e, pageX, pageY, null);
	};

	/**
	 * @method _updatePointerPosition
	 * @protected
	 * @param {Number} id
	 * @param {Event} e
	 * @param {Number} pageX
	 * @param {Number} pageY
	 **/
	p._updatePointerPosition = function(id, e, pageX, pageY) {
		var rect = this._getElementRect(this.canvas);
		pageX -= rect.left;
		pageY -= rect.top;

		var w = this.canvas.width;
		var h = this.canvas.height;
		pageX /= (rect.right-rect.left)/w;
		pageY /= (rect.bottom-rect.top)/h;
		var o = this._getPointerData(id);
		if (o.inBounds = (pageX >= 0 && pageY >= 0 && pageX <= w-1 && pageY <= h-1)) {
			o.x = pageX;
			o.y = pageY;
		} else if (this.mouseMoveOutside) {
			o.x = pageX < 0 ? 0 : (pageX > w-1 ? w-1 : pageX);
			o.y = pageY < 0 ? 0 : (pageY > h-1 ? h-1 : pageY);
		}

		o.posEvtObj = e;
		o.rawX = pageX;
		o.rawY = pageY;

		if (id === this._primaryPointerID || id === -1) {
			this.mouseX = o.x;
			this.mouseY = o.y;
			this.mouseInBounds = o.inBounds;
		}
	};

	/**
	 * @method _handleMouseUp
	 * @protected
	 * @param {MouseEvent} e
	 **/
	p._handleMouseUp = function(e) {
		this._handlePointerUp(-1, e, false);
	};

	/**
	 * @method _handlePointerUp
	 * @protected
	 * @param {Number} id
	 * @param {Event} e
	 * @param {Boolean} clear
	 * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
	 **/
	p._handlePointerUp = function(id, e, clear, owner) {
		var nextStage = this._nextStage, o = this._getPointerData(id);
		if (this._prevStage && owner === undefined) { return; } // redundant listener.
		
		var target=null, oTarget = o.target;
		if (!owner && (oTarget || nextStage)) { target = this._getObjectsUnderPoint(o.x, o.y, null, true); }
		
		if (o.down) { this._dispatchMouseEvent(this, "stagemouseup", false, id, o, e, target); o.down = false; }
		
		if (target == oTarget) { this._dispatchMouseEvent(oTarget, "click", true, id, o, e); }
		this._dispatchMouseEvent(oTarget, "pressup", true, id, o, e);
		
		if (clear) {
			if (id==this._primaryPointerID) { this._primaryPointerID = null; }
			delete(this._pointerData[id]);
		} else { o.target = null; }
		
		nextStage&&nextStage._handlePointerUp(id, e, clear, owner || target && this);
	};

	/**
	 * @method _handleMouseDown
	 * @protected
	 * @param {MouseEvent} e
	 **/
	p._handleMouseDown = function(e) {
		this._handlePointerDown(-1, e, e.pageX, e.pageY);
	};

	/**
	 * @method _handlePointerDown
	 * @protected
	 * @param {Number} id
	 * @param {Event} e
	 * @param {Number} pageX
	 * @param {Number} pageY
	 * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
	 **/
	p._handlePointerDown = function(id, e, pageX, pageY, owner) {
		if (this.preventSelection) { e.preventDefault(); }
		if (this._primaryPointerID == null || id === -1) { this._primaryPointerID = id; } // mouse always takes over.
		
		if (pageY != null) { this._updatePointerPosition(id, e, pageX, pageY); }
		var target = null, nextStage = this._nextStage, o = this._getPointerData(id);
		if (!owner) { target = o.target = this._getObjectsUnderPoint(o.x, o.y, null, true); }

		if (o.inBounds) { this._dispatchMouseEvent(this, "stagemousedown", false, id, o, e, target); o.down = true; }
		this._dispatchMouseEvent(target, "mousedown", true, id, o, e);
		
		nextStage&&nextStage._handlePointerDown(id, e, pageX, pageY, owner || target && this);
	};

	/**
	 * @method _testMouseOver
	 * @param {Boolean} clear If true, clears the mouseover / rollover (ie. no target)
	 * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
	 * @param {Stage} eventTarget The stage that the cursor is actively over.
	 * @protected
	 **/
	p._testMouseOver = function(clear, owner, eventTarget) {
		if (this._prevStage && owner === undefined) { return; } // redundant listener.
		
		var nextStage = this._nextStage;
		if (!this._mouseOverIntervalID) {
			// not enabled for mouseover, but should still relay the event.
			nextStage&&nextStage._testMouseOver(clear, owner, eventTarget);
			return;
		}
		var o = this._getPointerData(-1);
		// only update if the mouse position has changed. This provides a lot of optimization, but has some trade-offs.
		if (!o || (!clear && this.mouseX == this._mouseOverX && this.mouseY == this._mouseOverY && this.mouseInBounds)) { return; }
		
		var e = o.posEvtObj;
		var isEventTarget = eventTarget || e&&(e.target == this.canvas);
		var target=null, common = -1, cursor="", t, i, l;
		
		if (!owner && (clear || this.mouseInBounds && isEventTarget)) {
			target = this._getObjectsUnderPoint(this.mouseX, this.mouseY, null, true);
			this._mouseOverX = this.mouseX;
			this._mouseOverY = this.mouseY;
		}

		var oldList = this._mouseOverTarget||[];
		var oldTarget = oldList[oldList.length-1];
		var list = this._mouseOverTarget = [];

		// generate ancestor list and check for cursor:
		t = target;
		while (t) {
			list.unshift(t);
			if (!cursor) { cursor = t.cursor; }
			t = t.parent;
		}
		this.canvas.style.cursor = cursor;
		if (!owner && eventTarget) { eventTarget.canvas.style.cursor = cursor; }

		// find common ancestor:
		for (i=0,l=list.length; i<l; i++) {
			if (list[i] != oldList[i]) { break; }
			common = i;
		}

		if (oldTarget != target) {
			this._dispatchMouseEvent(oldTarget, "mouseout", true, -1, o, e, target);
		}

		for (i=oldList.length-1; i>common; i--) {
			this._dispatchMouseEvent(oldList[i], "rollout", false, -1, o, e, target);
		}

		for (i=list.length-1; i>common; i--) {
			this._dispatchMouseEvent(list[i], "rollover", false, -1, o, e, oldTarget);
		}

		if (oldTarget != target) {
			this._dispatchMouseEvent(target, "mouseover", true, -1, o, e, oldTarget);
		}
		
		nextStage&&nextStage._testMouseOver(clear, owner || target && this, eventTarget || isEventTarget && this);
	};

	/**
	 * @method _handleDoubleClick
	 * @protected
	 * @param {MouseEvent} e
	 * @param {Stage} owner Indicates that the event has already been captured & handled by the indicated stage.
	 **/
	p._handleDoubleClick = function(e, owner) {
		var target=null, nextStage=this._nextStage, o=this._getPointerData(-1);
		if (!owner) {
			target = this._getObjectsUnderPoint(o.x, o.y, null, true);
			this._dispatchMouseEvent(target, "dblclick", true, -1, o, e);
		}
		nextStage&&nextStage._handleDoubleClick(e, owner || target && this);
	};

	/**
	 * @method _dispatchMouseEvent
	 * @protected
	 * @param {DisplayObject} target
	 * @param {String} type
	 * @param {Boolean} bubbles
	 * @param {Number} pointerId
	 * @param {Object} o
	 * @param {MouseEvent} [nativeEvent]
	 * @param {DisplayObject} [relatedTarget]
	 **/
	p._dispatchMouseEvent = function(target, type, bubbles, pointerId, o, nativeEvent, relatedTarget) {
		// TODO: might be worth either reusing MouseEvent instances, or adding a willTrigger method to avoid GC.
		if (!target || (!bubbles && !target.hasEventListener(type))) { return; }
		/*
		// TODO: account for stage transformations?
		this._mtx = this.getConcatenatedMatrix(this._mtx).invert();
		var pt = this._mtx.transformPoint(o.x, o.y);
		var evt = new createjs.MouseEvent(type, bubbles, false, pt.x, pt.y, nativeEvent, pointerId, pointerId==this._primaryPointerID || pointerId==-1, o.rawX, o.rawY);
		*/
		var evt = new createjs.MouseEvent(type, bubbles, false, o.x, o.y, nativeEvent, pointerId, pointerId === this._primaryPointerID || pointerId === -1, o.rawX, o.rawY, relatedTarget);
		target.dispatchEvent(evt);
	};


	createjs.Stage = createjs.promote(Stage, "Container");
}());

//##############################################################################
// Bitmap.js
//##############################################################################

(function() {
	
	/**
	 * A Bitmap represents an Image, Canvas, or Video in the display list. A Bitmap can be instantiated using an existing
	 * HTML element, or a string.
	 *
	 * <h4>Example</h4>
	 *
	 *      var bitmap = new createjs.Bitmap("imagePath.jpg");
	 *
	 * <strong>Notes:</strong>
	 * <ol>
	 *     <li>When a string path or image tag that is not yet loaded is used, the stage may need to be redrawn before it
	 *      will be displayed.</li>
	 *     <li>Bitmaps with an SVG source currently will not respect an alpha value other than 0 or 1. To get around this,
	 *     the Bitmap can be cached.</li>
	 *     <li>Bitmaps with an SVG source will taint the canvas with cross-origin data, which prevents interactivity. This
	 *     happens in all browsers except recent Firefox builds.</li>
	 *     <li>Images loaded cross-origin will throw cross-origin security errors when interacted with using a mouse, using
	 *     methods such as `getObjectUnderPoint`, or using filters, or caching. You can get around this by setting
	 *     `crossOrigin` flags on your images before passing them to EaselJS, eg: `img.crossOrigin="Anonymous";`</li>
	 * </ol>
	 *
	 * @class Bitmap
	 * @extends DisplayObject
	 * @constructor
	 * @param {HTMLImageElement | HTMLCanvasElement | HTMLVideoElement | String} imageOrUri The source object or URI to an image to
	 * display. This can be either an Image, Canvas, or Video object, or a string URI to an image file to load and use.
	 * If it is a URI, a new Image object will be constructed and assigned to the .image property.
	 **/
	function Bitmap(imageOrUri) {
		this.DisplayObject_constructor();
		
		
	// public properties:
		/**
		 * The image to render. This can be an Image, a Canvas, or a Video. Not all browsers (especially
		 * mobile browsers) support drawing video to a canvas.
		 * @property image
		 * @type HTMLImageElement | HTMLCanvasElement | HTMLVideoElement
		 **/
		if (typeof imageOrUri == "string") {
			this.image = document.createElement("img");
			this.image.src = imageOrUri;
		} else {
			this.image = imageOrUri;
		}
	
		/**
		 * Specifies an area of the source image to draw. If omitted, the whole image will be drawn.
		 * Note that video sources must have a width / height set to work correctly with `sourceRect`.
		 * @property sourceRect
		 * @type Rectangle
		 * @default null
		 */
		this.sourceRect = null;
	}
	var p = createjs.extend(Bitmap, createjs.DisplayObject);
	
	
// public methods:
	/**
	 * Constructor alias for backwards compatibility. This method will be removed in future versions.
	 * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
	 * @method initialize
	 * @deprecated in favour of `createjs.promote()`
	 **/
	p.initialize = Bitmap; // TODO: deprecated.

	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var image = this.image;
		var hasContent = this.cacheCanvas || (image && (image.naturalWidth || image.getContext || image.readyState >= 2));
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 *
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 * @return {Boolean}
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache) || !this.image) { return true; }
		var img = this.image, rect = this.sourceRect;
		if (rect) {
			// some browsers choke on out of bound values, so we'll fix them:
			var x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height, x = 0, y = 0, w = img.width, h = img.height;
			if (x1 < 0) { x -= x1; x1 = 0; }
			if (x2 > w) { x2 = w; }
			if (y1 < 0) { y -= y1; y1 = 0; }
			if (y2 > h) { y2 = h; }
			ctx.drawImage(img, x1, y1, x2-x1, y2-y1, x, y, x2-x1, y2-y1);
		} else {
			ctx.drawImage(img, 0, 0);
		}
		return true;
	};
	
	//Note, the doc sections below document using the specified APIs (from DisplayObject)  from
	//Bitmap. This is why they have no method implementations.
	
	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should <b>not</b> cache Bitmap instances as it can degrade performance.
	 *
	 * <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method cache
	 **/
	
	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should <b>not</b> cache Bitmap instances as it can degrade performance.
	 *
	 * <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method updateCache
	 **/
	
	/**
	 * Because the content of a Bitmap is already in a simple format, cache is unnecessary for Bitmap instances.
	 * You should <b>not</b> cache Bitmap instances as it can degrade performance.
	 *
	 * <strong>However: If you want to use a filter on a Bitmap, you <em>MUST</em> cache it, or it will not work.</strong>
	 * To see the API for caching, please visit the DisplayObject {{#crossLink "DisplayObject/cache"}}{{/crossLink}}
	 * method.
	 * @method uncache
	 **/

	/**
	 * Docced in superclass.
	 */
	p.getBounds = function() {
		var rect = this.DisplayObject_getBounds();
		if (rect) { return rect; }
		var image = this.image, o = this.sourceRect || image;
		var hasContent = (image && (image.naturalWidth || image.getContext || image.readyState >= 2));
		return hasContent ? this._rectangle.setValues(0, 0, o.width, o.height) : null;
	};
	
	/**
	 * Returns a clone of the Bitmap instance.
	 * @method clone
	 * @return {Bitmap} a clone of the Bitmap instance.
	 **/
	p.clone = function() {
		var o = new Bitmap(this.image);
		if (this.sourceRect) { o.sourceRect = this.sourceRect.clone(); }
		this._cloneProps(o);
		return o;
	};
	
	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Bitmap (name="+  this.name +")]";
	};

	
	createjs.Bitmap = createjs.promote(Bitmap, "DisplayObject");
}());

//##############################################################################
// Sprite.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Displays a frame or sequence of frames (ie. an animation) from a SpriteSheet instance. A sprite sheet is a series of
	 * images (usually animation frames) combined into a single image. For example, an animation consisting of 8 100x100
	 * images could be combined into a 400x200 sprite sheet (4 frames across by 2 high). You can display individual frames,
	 * play frames as an animation, and even sequence animations together.
	 *
	 * See the {{#crossLink "SpriteSheet"}}{{/crossLink}} class for more information on setting up frames and animations.
	 *
	 * <h4>Example</h4>
	 *
	 *      var instance = new createjs.Sprite(spriteSheet);
	 *      instance.gotoAndStop("frameName");
	 *
	 * Until {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} or {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} is called,
	 * only the first defined frame defined in the sprite sheet will be displayed.
	 *
	 * @class Sprite
	 * @extends DisplayObject
	 * @constructor
	 * @param {SpriteSheet} spriteSheet The SpriteSheet instance to play back. This includes the source image(s), frame
	 * dimensions, and frame data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
	 * @param {String|Number} [frameOrAnimation] The frame number or animation to play initially.
	 **/
	function Sprite(spriteSheet, frameOrAnimation) {
		this.DisplayObject_constructor();
		
		
	// public properties:
		/**
		 * The frame index that will be drawn when draw is called. Note that with some {{#crossLink "SpriteSheet"}}{{/crossLink}}
		 * definitions, this will advance non-sequentially. This will always be an integer value.
		 * @property currentFrame
		 * @type {Number}
		 * @default 0
		 * @readonly
		 **/
		this.currentFrame = 0;
	
		/**
		 * Returns the name of the currently playing animation.
		 * @property currentAnimation
		 * @type {String}
		 * @final
		 * @readonly
		 **/
		this.currentAnimation = null;
	
		/**
		 * Prevents the animation from advancing each tick automatically. For example, you could create a sprite
		 * sheet of icons, set paused to true, and display the appropriate icon by setting <code>currentFrame</code>.
		 * @property paused
		 * @type {Boolean}
		 * @default false
		 **/
		this.paused = true;
	
		/**
		 * The SpriteSheet instance to play back. This includes the source image, frame dimensions, and frame
		 * data. See {{#crossLink "SpriteSheet"}}{{/crossLink}} for more information.
		 * @property spriteSheet
		 * @type {SpriteSheet}
		 * @readonly
		 **/
		this.spriteSheet = spriteSheet;
	
		/**
		 * Specifies the current frame index within the currently playing animation. When playing normally, this will increase
		 * from 0 to n-1, where n is the number of frames in the current animation.
		 *
		 * This could be a non-integer value if
		 * using time-based playback (see {{#crossLink "Sprite/framerate"}}{{/crossLink}}, or if the animation's speed is
		 * not an integer.
		 * @property currentAnimationFrame
		 * @type {Number}
		 * @default 0
		 **/
		this.currentAnimationFrame = 0;
	
		/**
		 * By default Sprite instances advance one frame per tick. Specifying a framerate for the Sprite (or its related
		 * SpriteSheet) will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
		 * framerate.
		 *
		 * For example, if a Sprite with a framerate of 10 is placed on a Stage being updated at 40fps, then the Sprite will
		 * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
		 * vary slightly between frames.
		 *
		 * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
		 * passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
		 * @property framerate
		 * @type {Number}
		 * @default 0
		 **/
		this.framerate = 0;
	
	
	// private properties:
		/**
		 * Current animation object.
		 * @property _animation
		 * @protected
		 * @type {Object}
		 * @default null
		 **/
		this._animation = null;
	
		/**
		 * Current frame index.
		 * @property _currentFrame
		 * @protected
		 * @type {Number}
		 * @default null
		 **/
		this._currentFrame = null;
		
		/**
		 * Skips the next auto advance. Used by gotoAndPlay to avoid immediately jumping to the next frame
		 * @property _skipAdvance
		 * @protected
		 * @type {Boolean}
		 * @default false
		 **/
		this._skipAdvance = false;
		
		
		if (frameOrAnimation != null) { this.gotoAndPlay(frameOrAnimation); }
	}
	var p = createjs.extend(Sprite, createjs.DisplayObject);

	/**
	 * Constructor alias for backwards compatibility. This method will be removed in future versions.
	 * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
	 * @method initialize
	 * @deprecated in favour of `createjs.promote()`
	 **/
	p.initialize = Sprite; // TODO: Deprecated. This is for backwards support of FlashCC spritesheet export.


// events:
	/**
	 * Dispatched when an animation reaches its ends.
	 * @event animationend
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {String} name The name of the animation that just ended.
	 * @param {String} next The name of the next animation that will be played, or null. This will be the same as name if the animation is looping.
	 * @since 0.6.0
	 */
	 
	/**
	 * Dispatched any time the current frame changes. For example, this could be due to automatic advancement on a tick,
	 * or calling gotoAndPlay() or gotoAndStop().
	 * @event change
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 */


// public methods:
	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || this.spriteSheet.complete;
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
		this._normalizeFrame();
		var o = this.spriteSheet.getFrame(this._currentFrame|0);
		if (!o) { return false; }
		var rect = o.rect;
		if (rect.width && rect.height) { ctx.drawImage(o.image, rect.x, rect.y, rect.width, rect.height, -o.regX, -o.regY, rect.width, rect.height); }
		return true;
	};

	//Note, the doc sections below document using the specified APIs (from DisplayObject)  from
	//Bitmap. This is why they have no method implementations.

	/**
	 * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
	 * You should not cache Sprite instances as it can degrade performance.
	 * @method cache
	 **/

	/**
	 * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
	 * You should not cache Sprite instances as it can degrade performance.
	 * @method updateCache
	 **/

	/**
	 * Because the content of a Sprite is already in a raster format, cache is unnecessary for Sprite instances.
	 * You should not cache Sprite instances as it can degrade performance.
	 * @method uncache
	 **/

	/**
	 * Play (unpause) the current animation. The Sprite will be paused if either {{#crossLink "Sprite/stop"}}{{/crossLink}}
	 * or {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} is called. Single frame animations will remain
	 * unchanged.
	 * @method play
	 **/
	p.play = function() {
		this.paused = false;
	};

	/**
	 * Stop playing a running animation. The Sprite will be playing if {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}
	 * is called. Note that calling {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}} or {{#crossLink "Sprite/play"}}{{/crossLink}}
	 * will resume playback.
	 * @method stop
	 **/
	p.stop = function() {
		this.paused = true;
	};

	/**
	 * Sets paused to false and plays the specified animation name, named frame, or frame number.
	 * @method gotoAndPlay
	 * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
	 * and begin playing.
	 **/
	p.gotoAndPlay = function(frameOrAnimation) {
		this.paused = false;
		this._skipAdvance = true;
		this._goto(frameOrAnimation);
	};

	/**
	 * Sets paused to true and seeks to the specified animation name, named frame, or frame number.
	 * @method gotoAndStop
	 * @param {String|Number} frameOrAnimation The frame number or animation name that the playhead should move to
	 * and stop.
	 **/
	p.gotoAndStop = function(frameOrAnimation) {
		this.paused = true;
		this._goto(frameOrAnimation);
	};

	/**
	 * Advances the playhead. This occurs automatically each tick by default.
	 * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set on the Sprite
	 * or its SpriteSheet.
	 * @method advance
	*/
	p.advance = function(time) {
		var fps = this.framerate || this.spriteSheet.framerate;
		var t = (fps && time != null) ? time/(1000/fps) : 1;
		this._normalizeFrame(t);
	};
	
	/**
	 * Returns a {{#crossLink "Rectangle"}}{{/crossLink}} instance defining the bounds of the current frame relative to
	 * the origin. For example, a 90 x 70 frame with <code>regX=50</code> and <code>regY=40</code> would return a
	 * rectangle with [x=-50, y=-40, width=90, height=70]. This ignores transformations on the display object.
	 *
	 * Also see the SpriteSheet {{#crossLink "SpriteSheet/getFrameBounds"}}{{/crossLink}} method.
	 * @method getBounds
	 * @return {Rectangle} A Rectangle instance. Returns null if the frame does not exist, or the image is not fully
	 * loaded.
	 **/
	p.getBounds = function() {
		// TODO: should this normalizeFrame?
		return this.DisplayObject_getBounds() || this.spriteSheet.getFrameBounds(this.currentFrame, this._rectangle);
	};

	/**
	 * Returns a clone of the Sprite instance. Note that the same SpriteSheet is shared between cloned
	 * instances.
	 * @method clone
	 * @return {Sprite} a clone of the Sprite instance.
	 **/
	p.clone = function() {
		return this._cloneProps(new Sprite(this.spriteSheet));
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Sprite (name="+  this.name +")]";
	};

// private methods:
	/**
	 * @method _cloneProps
	 * @param {Sprite} o
	 * @return {Sprite} o
	 * @protected
	 **/
	p._cloneProps = function(o) {
		this.DisplayObject__cloneProps(o);
		o.currentFrame = this.currentFrame;
		o.currentAnimation = this.currentAnimation;
		o.paused = this.paused;
		o.currentAnimationFrame = this.currentAnimationFrame;
		o.framerate = this.framerate;
		
		o._animation = this._animation;
		o._currentFrame = this._currentFrame;
		o._skipAdvance = this._skipAdvance;
		return o;
	};
	
	/**
	 * Advances the <code>currentFrame</code> if paused is not true. This is called automatically when the {{#crossLink "Stage"}}{{/crossLink}}
	 * ticks.
	 * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
	 * @protected
	 * @method _tick
	 **/
	p._tick = function(evtObj) {
		if (!this.paused) {
			if (!this._skipAdvance) { this.advance(evtObj&&evtObj.delta); }
			this._skipAdvance = false;
		}
		this.DisplayObject__tick(evtObj);
	};


	/**
	 * Normalizes the current frame, advancing animations and dispatching callbacks as appropriate.
	 * @protected
	 * @method _normalizeFrame
	 **/
	p._normalizeFrame = function(frameDelta) {
		frameDelta = frameDelta || 0;
		var animation = this._animation;
		var paused = this.paused;
		var frame = this._currentFrame;
		var l;
		
		if (animation) {
			var speed = animation.speed || 1;
			var animFrame = this.currentAnimationFrame;
			l = animation.frames.length;
			if (animFrame + frameDelta * speed >= l) {
				var next = animation.next;
				if (this._dispatchAnimationEnd(animation, frame, paused, next, l - 1)) {
					// something changed in the event stack, so we shouldn't make any more changes here.
					return;
				} else if (next) {
					// sequence. Automatically calls _normalizeFrame again with the remaining frames.
					return this._goto(next, frameDelta - (l - animFrame) / speed);
				} else {
					// end.
					this.paused = true;
					animFrame = animation.frames.length - 1;
				}
			} else {
				animFrame += frameDelta * speed;
			}
			this.currentAnimationFrame = animFrame;
			this._currentFrame = animation.frames[animFrame | 0]
		} else {
			frame = (this._currentFrame += frameDelta);
			l = this.spriteSheet.getNumFrames();
			if (frame >= l && l > 0) {
				if (!this._dispatchAnimationEnd(animation, frame, paused, l - 1)) {
					// looped.
					if ((this._currentFrame -= l) >= l) { return this._normalizeFrame(); }
				}
			}
		}
		frame = this._currentFrame | 0;
		if (this.currentFrame != frame) {
			this.currentFrame = frame;
			this.dispatchEvent("change");
		}
	};

	/**
	 * Dispatches the "animationend" event. Returns true if a handler changed the animation (ex. calling {{#crossLink "Sprite/stop"}}{{/crossLink}},
	 * {{#crossLink "Sprite/gotoAndPlay"}}{{/crossLink}}, etc.)
	 * @property _dispatchAnimationEnd
	 * @private
	 * @type {Function}
	 **/
	p._dispatchAnimationEnd = function(animation, frame, paused, next, end) {
		var name = animation ? animation.name : null;
		if (this.hasEventListener("animationend")) {
			var evt = new createjs.Event("animationend");
			evt.name = name;
			evt.next = next;
			this.dispatchEvent(evt);
		}
		// did the animation get changed in the event stack?:
		var changed = (this._animation != animation || this._currentFrame != frame);
		// if the animation hasn't changed, but the sprite was paused, then we want to stick to the last frame:
		if (!changed && !paused && this.paused) { this.currentAnimationFrame = end; changed = true; }
		return changed;
	};

	/**
	 * Moves the playhead to the specified frame number or animation.
	 * @method _goto
	 * @param {String|Number} frameOrAnimation The frame number or animation that the playhead should move to.
	 * @param {Boolean} [frame] The frame of the animation to go to. Defaults to 0.
	 * @protected
	 **/
	p._goto = function(frameOrAnimation, frame) {
		this.currentAnimationFrame = 0;
		if (isNaN(frameOrAnimation)) {
			var data = this.spriteSheet.getAnimation(frameOrAnimation);
			if (data) {
				this._animation = data;
				this.currentAnimation = frameOrAnimation;
				this._normalizeFrame(frame);
			}
		} else {
			this.currentAnimation = this._animation = null;
			this._currentFrame = frameOrAnimation;
			this._normalizeFrame();
		}
	};


	createjs.Sprite = createjs.promote(Sprite, "DisplayObject");
}());

//##############################################################################
// Shape.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * A Shape allows you to display vector art in the display list. It composites a {{#crossLink "Graphics"}}{{/crossLink}}
	 * instance which exposes all of the vector drawing methods. The Graphics instance can be shared between multiple Shape
	 * instances to display the same vector graphics with different positions or transforms.
	 *
	 * If the vector art will not
	 * change between draws, you may want to use the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method to reduce the
	 * rendering cost.
	 *
	 * <h4>Example</h4>
	 *
	 *      var graphics = new createjs.Graphics().beginFill("#ff0000").drawRect(0, 0, 100, 100);
	 *      var shape = new createjs.Shape(graphics);
	 *
	 *      //Alternatively use can also use the graphics property of the Shape class to renderer the same as above.
	 *      var shape = new createjs.Shape();
	 *      shape.graphics.beginFill("#ff0000").drawRect(0, 0, 100, 100);
	 *
	 * @class Shape
	 * @extends DisplayObject
	 * @constructor
	 * @param {Graphics} graphics Optional. The graphics instance to display. If null, a new Graphics instance will be created.
	 **/
	function Shape(graphics) {
		this.DisplayObject_constructor();
		
		
	// public properties:
		/**
		 * The graphics instance to display.
		 * @property graphics
		 * @type Graphics
		 **/
		this.graphics = graphics ? graphics : new createjs.Graphics();
	}
	var p = createjs.extend(Shape, createjs.DisplayObject);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// public methods:
	/**
	 * Returns true or false indicating whether the Shape would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the Shape would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || (this.graphics && !this.graphics.isEmpty());
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	};

	/**
	 * Draws the Shape into the specified context ignoring its visible, alpha, shadow, and transform. Returns true if
	 * the draw was handled (useful for overriding functionality).
	 *
	 * <i>NOTE: This method is mainly for internal use, though it may be useful for advanced uses.</i>
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} [ignoreCache=false] Indicates whether the draw operation should ignore any current cache. For example,
	 * used for drawing the cache (to prevent it from simply drawing an existing cache back into itself).
	 * @return {Boolean}
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
		this.graphics.draw(ctx, this);
		return true;
	};

	/**
	 * Returns a clone of this Shape. Some properties that are specific to this instance's current context are reverted to
	 * their defaults (for example .parent).
	 * @method clone
	 * @param {Boolean} recursive If true, this Shape's {{#crossLink "Graphics"}}{{/crossLink}} instance will also be
	 * cloned. If false, the Graphics instance will be shared with the new Shape.
	 **/
	p.clone = function(recursive) {
		var g = (recursive && this.graphics) ? this.graphics.clone() : this.graphics;
		return  this._cloneProps(new Shape(g));
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Shape (name="+  this.name +")]";
	};


	createjs.Shape = createjs.promote(Shape, "DisplayObject");
}());

//##############################################################################
// Text.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Display one or more lines of dynamic text (not user editable) in the display list. Line wrapping support (using the
	 * lineWidth) is very basic, wrapping on spaces and tabs only. Note that as an alternative to Text, you can position HTML
	 * text above or below the canvas relative to items in the display list using the {{#crossLink "DisplayObject/localToGlobal"}}{{/crossLink}}
	 * method, or using {{#crossLink "DOMElement"}}{{/crossLink}}.
	 *
	 * <b>Please note that Text does not support HTML text, and can only display one font style at a time.</b> To use
	 * multiple font styles, you will need to create multiple text instances, and position them manually.
	 *
	 * <h4>Example</h4>
	 *
	 *      var text = new createjs.Text("Hello World", "20px Arial", "#ff7700");
	 *      text.x = 100;
	 *      text.textBaseline = "alphabetic";
	 *
	 * CreateJS Text supports web fonts (the same rules as Canvas). The font must be loaded and supported by the browser
	 * before it can be displayed.
	 *
	 * <strong>Note:</strong> Text can be expensive to generate, so cache instances where possible. Be aware that not all
	 * browsers will render Text exactly the same.
	 * @class Text
	 * @extends DisplayObject
	 * @constructor
	 * @param {String} [text] The text to display.
	 * @param {String} [font] The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold
	 * 36px Arial").
	 * @param {String} [color] The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex.
	 * "#F00", "red", or "#FF0000").
	 **/
	function Text(text, font, color) {
		this.DisplayObject_constructor();
		
		
	// public properties:
		/**
		 * The text to display.
		 * @property text
		 * @type String
		 **/
		this.text = text;
	
		/**
		 * The font style to use. Any valid value for the CSS font attribute is acceptable (ex. "bold 36px Arial").
		 * @property font
		 * @type String
		 **/
		this.font = font;
	
		/**
		 * The color to draw the text in. Any valid value for the CSS color attribute is acceptable (ex. "#F00"). Default is "#000".
		 * It will also accept valid canvas fillStyle values.
		 * @property color
		 * @type String
		 **/
		this.color = color;
	
		/**
		 * The horizontal text alignment. Any of "start", "end", "left", "right", and "center". For detailed
		 * information view the
		 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
		 * whatwg spec</a>. Default is "left".
		 * @property textAlign
		 * @type String
		 **/
		this.textAlign = "left";
	
		/**
		 * The vertical alignment point on the font. Any of "top", "hanging", "middle", "alphabetic", "ideographic", or
		 * "bottom". For detailed information view the <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
		 * whatwg spec</a>. Default is "top".
		 * @property textBaseline
		 * @type String
		*/
		this.textBaseline = "top";
	
		/**
		 * The maximum width to draw the text. If maxWidth is specified (not null), the text will be condensed or
		 * shrunk to make it fit in this width. For detailed information view the
		 * <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#text-styles">
		 * whatwg spec</a>.
		 * @property maxWidth
		 * @type Number
		*/
		this.maxWidth = null;
	
		/**
		 * If greater than 0, the text will be drawn as a stroke (outline) of the specified width.
		 * @property outline
		 * @type Number
		 **/
		this.outline = 0;
	
		/**
		 * Indicates the line height (vertical distance between baselines) for multi-line text. If null or 0,
		 * the value of getMeasuredLineHeight is used.
		 * @property lineHeight
		 * @type Number
		 **/
		this.lineHeight = 0;
	
		/**
		 * Indicates the maximum width for a line of text before it is wrapped to multiple lines. If null,
		 * the text will not be wrapped.
		 * @property lineWidth
		 * @type Number
		 **/
		this.lineWidth = null;
	}
	var p = createjs.extend(Text, createjs.DisplayObject);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.

	
// static properties:
	/**
	 * @property _workingContext
	 * @type CanvasRenderingContext2D
	 * @private
	 **/
	var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
	if (canvas.getContext) { Text._workingContext = canvas.getContext("2d"); canvas.width = canvas.height = 1; }
	
	
// constants:
	/**
	 * Lookup table for the ratio to offset bounds x calculations based on the textAlign property.
	 * @property H_OFFSETS
	 * @type Object
	 * @protected
	 * @static
	 **/
	Text.H_OFFSETS = {start: 0, left: 0, center: -0.5, end: -1, right: -1};
	
	/**
	 * Lookup table for the ratio to offset bounds y calculations based on the textBaseline property.
	 * @property H_OFFSETS
	 * @type Object
	 * @protected
	 * @static
	 **/
	Text.V_OFFSETS = {top: 0, hanging: -0.01, middle: -0.4, alphabetic: -0.8, ideographic: -0.85, bottom: -1};


// public methods:
	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || (this.text != null && this.text !== "");
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0 && hasContent);
	};

	/**
	 * Draws the Text into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }

		var col = this.color || "#000";
		if (this.outline) { ctx.strokeStyle = col; ctx.lineWidth = this.outline*1; }
		else { ctx.fillStyle = col; }
		
		this._drawText(this._prepContext(ctx));
		return true;
	};

	/**
	 * Returns the measured, untransformed width of the text without wrapping. Use getBounds for a more robust value.
	 * @method getMeasuredWidth
	 * @return {Number} The measured, untransformed width of the text.
	 **/
	p.getMeasuredWidth = function() {
		return this._getMeasuredWidth(this.text);
	};

	/**
	 * Returns an approximate line height of the text, ignoring the lineHeight property. This is based on the measured
	 * width of a "M" character multiplied by 1.2, which provides an approximate line height for most fonts.
	 * @method getMeasuredLineHeight
	 * @return {Number} an approximate line height of the text, ignoring the lineHeight property. This is
	 * based on the measured width of a "M" character multiplied by 1.2, which approximates em for most fonts.
	 **/
	p.getMeasuredLineHeight = function() {
		return this._getMeasuredWidth("M")*1.2;
	};

	/**
	 * Returns the approximate height of multi-line text by multiplying the number of lines against either the
	 * <code>lineHeight</code> (if specified) or {{#crossLink "Text/getMeasuredLineHeight"}}{{/crossLink}}. Note that
	 * this operation requires the text flowing logic to run, which has an associated CPU cost.
	 * @method getMeasuredHeight
	 * @return {Number} The approximate height of the untransformed multi-line text.
	 **/
	p.getMeasuredHeight = function() {
		return this._drawText(null,{}).height;
	};

	/**
	 * Docced in superclass.
	 */
	p.getBounds = function() {
		var rect = this.DisplayObject_getBounds();
		if (rect) { return rect; }
		if (this.text == null || this.text === "") { return null; }
		var o = this._drawText(null, {});
		var w = (this.maxWidth && this.maxWidth < o.width) ? this.maxWidth : o.width;
		var x = w * Text.H_OFFSETS[this.textAlign||"left"];
		var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
		var y = lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
		return this._rectangle.setValues(x, y, w, o.height);
	};
	
	/**
	 * Returns an object with width, height, and lines properties. The width and height are the visual width and height
	 * of the drawn text. The lines property contains an array of strings, one for
	 * each line of text that will be drawn, accounting for line breaks and wrapping. These strings have trailing
	 * whitespace removed.
	 * @method getMetrics
	 * @return {Object} An object with width, height, and lines properties.
	 **/
	p.getMetrics = function() {
		var o = {lines:[]};
		o.lineHeight = this.lineHeight || this.getMeasuredLineHeight();
		o.vOffset = o.lineHeight * Text.V_OFFSETS[this.textBaseline||"top"];
		return this._drawText(null, o, o.lines);
	};

	/**
	 * Returns a clone of the Text instance.
	 * @method clone
	 * @return {Text} a clone of the Text instance.
	 **/
	p.clone = function() {
		return this._cloneProps(new Text(this.text, this.font, this.color));
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Text (text="+  (this.text.length > 20 ? this.text.substr(0, 17)+"..." : this.text) +")]";
	};


// private methods:
	/**
	 * @method _cloneProps
	 * @param {Text} o
	 * @protected
	 * @return {Text} o
	 **/
	p._cloneProps = function(o) {
		this.DisplayObject__cloneProps(o);
		o.textAlign = this.textAlign;
		o.textBaseline = this.textBaseline;
		o.maxWidth = this.maxWidth;
		o.outline = this.outline;
		o.lineHeight = this.lineHeight;
		o.lineWidth = this.lineWidth;
		return o;
	};

	/**
	 * @method _getWorkingContext
	 * @param {CanvasRenderingContext2D} ctx
	 * @return {CanvasRenderingContext2D}
	 * @protected
	 **/
	p._prepContext = function(ctx) {
		ctx.font = this.font||"10px sans-serif";
		ctx.textAlign = this.textAlign||"left";
		ctx.textBaseline = this.textBaseline||"top";
		return ctx;
	};

	/**
	 * Draws multiline text.
	 * @method _drawText
	 * @param {CanvasRenderingContext2D} ctx
	 * @param {Object} o
	 * @param {Array} lines
	 * @return {Object}
	 * @protected
	 **/
	p._drawText = function(ctx, o, lines) {
		var paint = !!ctx;
		if (!paint) {
			ctx = Text._workingContext;
			ctx.save();
			this._prepContext(ctx);
		}
		var lineHeight = this.lineHeight||this.getMeasuredLineHeight();
		
		var maxW = 0, count = 0;
		var hardLines = String(this.text).split(/(?:\r\n|\r|\n)/);
		for (var i=0, l=hardLines.length; i<l; i++) {
			var str = hardLines[i];
			var w = null;
			
			if (this.lineWidth != null && (w = ctx.measureText(str).width) > this.lineWidth) {
				// text wrapping:
				var words = str.split(/(\s)/);
				str = words[0];
				w = ctx.measureText(str).width;
				
				for (var j=1, jl=words.length; j<jl; j+=2) {
					// Line needs to wrap:
					var wordW = ctx.measureText(words[j] + words[j+1]).width;
					if (w + wordW > this.lineWidth) {
						if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
						if (lines) { lines.push(str); }
						if (w > maxW) { maxW = w; }
						str = words[j+1];
						w = ctx.measureText(str).width;
						count++;
					} else {
						str += words[j] + words[j+1];
						w += wordW;
					}
				}
			}
			
			if (paint) { this._drawTextLine(ctx, str, count*lineHeight); }
			if (lines) { lines.push(str); }
			if (o && w == null) { w = ctx.measureText(str).width; }
			if (w > maxW) { maxW = w; }
			count++;
		}
		
		if (o) {
			o.width = maxW;
			o.height = count*lineHeight;
		}
		if (!paint) { ctx.restore(); }
		return o;
	};

	/**
	 * @method _drawTextLine
	 * @param {CanvasRenderingContext2D} ctx
	 * @param {String} text
	 * @param {Number} y
	 * @protected
	 **/
	p._drawTextLine = function(ctx, text, y) {
		// Chrome 17 will fail to draw the text if the last param is included but null, so we feed it a large value instead:
		if (this.outline) { ctx.strokeText(text, 0, y, this.maxWidth||0xFFFF); }
		else { ctx.fillText(text, 0, y, this.maxWidth||0xFFFF); }
	};
	
	
	/**
	 * @method _getMeasuredWidth
	 * @param {String} text
	 * @protected
	 **/
	p._getMeasuredWidth = function(text) {
		var ctx = Text._workingContext;
		ctx.save();
		var w = this._prepContext(ctx).measureText(text).width;
		ctx.restore();
		return w;
	};


	createjs.Text = createjs.promote(Text, "DisplayObject");
}());

//##############################################################################
// BitmapText.js
//##############################################################################

(function () {
	"use strict";


// constructor:
	/**
	 * Displays text using bitmap glyphs defined in a sprite sheet. Multi-line text is supported
	 * using new line characters, but automatic wrapping is not supported. See the 
	 * {{#crossLink "BitmapText/spriteSheet:property"}}{{/crossLink}}
	 * property for more information on defining glyphs.
	 * 
	 * <strong>Important:</strong> BitmapText extends Container, but is not designed to be used as one.
	 * As such, methods like addChild and removeChild are disabled.
	 * @class BitmapText
	 * @extends DisplayObject
	 * @param {String} [text=""] The text to display.
	 * @param {SpriteSheet} [spriteSheet=null] The spritesheet that defines the character glyphs.
	 * @constructor
	 **/
	function BitmapText(text, spriteSheet) {
		this.Container_constructor();
		
		
	// public properties:
		/**
		 * The text to display.
		 * @property text
		 * @type String
		 * @default ""
		 **/
		this.text = text||"";
		
		/**
		 * A SpriteSheet instance that defines the glyphs for this bitmap text. Each glyph/character
		 * should have a single frame animation defined in the sprite sheet named the same as
		 * corresponding character. For example, the following animation definition:
		 *
		 * 		"A": {frames: [0]}
		 *
		 * would indicate that the frame at index 0 of the spritesheet should be drawn for the "A" character. The short form
		 * is also acceptable:
		 * 
		 * 		"A": 0
		 *
		 * Note that if a character in the text is not found in the sprite sheet, it will also
		 * try to use the alternate case (upper or lower).
		 *
		 * See SpriteSheet for more information on defining sprite sheet data.
		 * @property spriteSheet
		 * @type SpriteSheet
		 * @default null
		 **/
		this.spriteSheet = spriteSheet;
	
		/**
		 * The height of each line of text. If 0, then it will use a line height calculated
		 * by checking for the height of the "1", "T", or "L" character (in that order). If
		 * those characters are not defined, it will use the height of the first frame of the
		 * sprite sheet.
		 * @property lineHeight
		 * @type Number
		 * @default 0
		 **/
		this.lineHeight = 0;
	
		/**
		 * This spacing (in pixels) will be added after each character in the output.
		 * @property letterSpacing
		 * @type Number
		 * @default 0
		 **/
		this.letterSpacing = 0;
	
		/**
		 * If a space character is not defined in the sprite sheet, then empty pixels equal to
		 * spaceWidth will be inserted instead. If 0, then it will use a value calculated
		 * by checking for the width of the "1", "l", "E", or "A" character (in that order). If
		 * those characters are not defined, it will use the width of the first frame of the
		 * sprite sheet.
		 * @property spaceWidth
		 * @type Number
		 * @default 0
		 **/
		this.spaceWidth = 0;
		
		
	// private properties:
	 	/**
		 * @property _oldProps
		 * @type Object
		 * @protected
		 **/
		this._oldProps = {text:0,spriteSheet:0,lineHeight:0,letterSpacing:0,spaceWidth:0};
	}
	var p = createjs.extend(BitmapText, createjs.Container);

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.

// static properties:
	/**
	 * BitmapText uses Sprite instances to draw text. To reduce the creation and destruction of instances (and thus garbage collection), it maintains
	 * an internal object pool of sprite instances to reuse. Increasing this value can cause more sprites to be
	 * retained, slightly increasing memory use, but reducing instantiation.
	 * @property maxPoolSize
	 * @type Number
	 * @static
	 * @default 100
	 **/
	BitmapText.maxPoolSize = 100;
	
	/**
	 * Sprite object pool.
	 * @type {Array}
	 * @static
	 * @private
	 */
	BitmapText._spritePool = [];

	
// public methods:
	/**
	 * Docced in superclass.
	 **/
	p.draw = function(ctx, ignoreCache) {
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return; }
		this._updateText();
		this.Container_draw(ctx, ignoreCache);
	};
	
	/**
	 * Docced in superclass.
	 **/
	p.getBounds = function() {
		this._updateText();
		return this.Container_getBounds();
	};
	
	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		var hasContent = this.cacheCanvas || (this.spriteSheet && this.spriteSheet.complete && this.text);
		return !!(this.visible && this.alpha > 0 && this.scaleX !== 0 && this.scaleY !== 0 && hasContent);
	};
	
	p.clone = function() {
		return this._cloneProps(new BitmapText(this.text, this.spriteSheet));
	};
	
	/**
	 * <strong>Disabled in BitmapText.</strong>
	 * @method addChild
	 **/
	/**
	 * <strong>Disabled in BitmapText.</strong>
	 * @method addChildAt
	 **/
	/**
	 * <strong>Disabled in BitmapText.</strong>
	 * @method removeChild
	 **/
	/**
	 * <strong>Disabled in BitmapText.</strong>
	 * @method removeChildAt
	 **/
	/**
	 * <strong>Disabled in BitmapText.</strong>
	 * @method removeAllChildren
	 **/
	p.addChild = p.addChildAt = p.removeChild = p.removeChildAt = p.removeAllChildren = function() {};


// private methods:
 	/**
	 * @method _cloneProps
	 * @param {BitmapText} o
	 * @return {BitmapText} o
	 * @protected
	 **/
	p._cloneProps = function(o) {
		this.Container__cloneProps(o);
		o.lineHeight = this.lineHeight;
		o.letterSpacing = this.letterSpacing;
		o.spaceWidth = this.spaceWidth;
		return o;
	};
	
	/**
	 * @method _getFrameIndex
	 * @param {String} character
	 * @param {SpriteSheet} spriteSheet
	 * @return {Number}
	 * @protected
	 **/
	p._getFrameIndex = function(character, spriteSheet) {
		var c, o = spriteSheet.getAnimation(character);
		if (!o) {
			(character != (c = character.toUpperCase())) || (character != (c = character.toLowerCase())) || (c=null);
			if (c) { o = spriteSheet.getAnimation(c); }
		}
		return o && o.frames[0];
	};
	
	/**
	 * @method _getFrame
	 * @param {String} character
	 * @param {SpriteSheet} spriteSheet
	 * @return {Object}
	 * @protected
	 **/
	p._getFrame = function(character, spriteSheet) {
		var index = this._getFrameIndex(character, spriteSheet);
		return index == null ? index : spriteSheet.getFrame(index);
	};
	
	/**
	 * @method _getLineHeight
	 * @param {SpriteSheet} ss
	 * @return {Number}
	 * @protected
	 **/
	p._getLineHeight = function(ss) {
		var frame = this._getFrame("1",ss) || this._getFrame("T",ss) || this._getFrame("L",ss) || ss.getFrame(0);
		return frame ? frame.rect.height : 1;
	};
	/**
	 * @method _getSpaceWidth
	 * @param {SpriteSheet} ss
	 * @return {Number}
	 * @protected
	 **/
	p._getSpaceWidth = function(ss) {
		var frame = this._getFrame("1",ss) || this._getFrame("l",ss) || this._getFrame("e",ss) || this._getFrame("a",ss) || ss.getFrame(0);
		return frame ? frame.rect.width : 1;
	};
	
	/**
	 * @method _drawText
	 * @protected
	 **/
	p._updateText = function() {
		var x=0, y=0, o=this._oldProps, change=false, spaceW=this.spaceWidth, lineH=this.lineHeight, ss=this.spriteSheet;
		var pool=BitmapText._spritePool, kids=this.children, childIndex=0, numKids=kids.length, sprite;
		
		for (var n in o) {
			if (o[n] != this[n]) {
				o[n] = this[n];
				change = true;
			}
		}
		if (!change) { return; }
		
		var hasSpace = !!this._getFrame(" ", ss);
		if (!hasSpace && !spaceW) { spaceW = this._getSpaceWidth(ss); }
		if (!lineH) { lineH = this._getLineHeight(ss); }
		
		for(var i=0, l=this.text.length; i<l; i++) {
			var character = this.text.charAt(i);
			if (character == " " && !hasSpace) {
				x += spaceW;
				continue;
			} else if (character=="\n" || character=="\r") {
				if (character=="\r" && this.text.charAt(i+1) == "\n") { i++; } // crlf
				x = 0;
				y += lineH;
				continue;
			}

			var index = this._getFrameIndex(character, ss);
			if (index == null) { continue; }
			
			if (childIndex < numKids) {
				sprite = kids[childIndex];
			} else {
				kids.push(sprite = pool.length ? pool.pop() : new createjs.Sprite());
				sprite.parent = this;
				numKids++;
			}
			sprite.spriteSheet = ss;
			sprite.gotoAndStop(index);
			sprite.x = x;
			sprite.y = y;
			childIndex++;
			
			x += sprite.getBounds().width + this.letterSpacing;
		}
		while (numKids > childIndex) {
			 // faster than removeChild.
			pool.push(sprite = kids.pop());
			sprite.parent = null;
			numKids--;
		}
		if (pool.length > BitmapText.maxPoolSize) { pool.length = BitmapText.maxPoolSize; }
	};


	createjs.BitmapText = createjs.promote(BitmapText, "Container");
}());

//##############################################################################
// MovieClip.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * The MovieClip class associates a TweenJS Timeline with an EaselJS {{#crossLink "Container"}}{{/crossLink}}. It allows
	 * you to create objects which encapsulate timeline animations, state changes, and synched actions. Due to the
	 * complexities inherent in correctly setting up a MovieClip, it is largely intended for tool output and is not included
	 * in the main EaselJS library.
	 *
	 * Currently MovieClip only works properly if it is tick based (as opposed to time based) though some concessions have
	 * been made to support time-based timelines in the future.
	 *
	 * <h4>Example</h4>
	 * This example animates two shapes back and forth. The grey shape starts on the left, but we jump to a mid-point in
	 * the animation using {{#crossLink "MovieClip/gotoAndPlay"}}{{/crossLink}}.
	 *
	 *      var stage = new createjs.Stage("canvas");
	 *      createjs.Ticker.addEventListener("tick", stage);
	 *
	 *      var mc = new createjs.MovieClip(null, 0, true, {start:20});
	 *      stage.addChild(mc);
	 *
	 *      var child1 = new createjs.Shape(
	 *          new createjs.Graphics().beginFill("#999999")
	 *              .drawCircle(30,30,30));
	 *      var child2 = new createjs.Shape(
	 *          new createjs.Graphics().beginFill("#5a9cfb")
	 *              .drawCircle(30,30,30));
	 *
	 *      mc.timeline.addTween(
	 *          createjs.Tween.get(child1)
	 *              .to({x:0}).to({x:60}, 50).to({x:0}, 50));
	 *      mc.timeline.addTween(
	 *          createjs.Tween.get(child2)
	 *              .to({x:60}).to({x:0}, 50).to({x:60}, 50));
	 *
	 *      mc.gotoAndPlay("start");
	 *
	 * It is recommended to use <code>tween.to()</code> to animate and set properties (use no duration to have it set
	 * immediately), and the <code>tween.wait()</code> method to create delays between animations. Note that using the
	 * <code>tween.set()</code> method to affect properties will likely not provide the desired result.
	 *
	 * @class MovieClip
	 * @main MovieClip
	 * @extends Container
	 * @constructor
	 * @param {String} [mode=independent] Initial value for the mode property. One of {{#crossLink "MovieClip/INDEPENDENT:property"}}{{/crossLink}},
	 * {{#crossLink "MovieClip/SINGLE_FRAME:property"}}{{/crossLink}}, or {{#crossLink "MovieClip/SYNCHED:property"}}{{/crossLink}}.
	 * The default is {{#crossLink "MovieClip/INDEPENDENT:property"}}{{/crossLink}}.
	 * @param {Number} [startPosition=0] Initial value for the {{#crossLink "MovieClip/startPosition:property"}}{{/crossLink}}
	 * property.
	 * @param {Boolean} [loop=true] Initial value for the {{#crossLink "MovieClip/loop:property"}}{{/crossLink}}
	 * property. The default is `true`.
	 * @param {Object} [labels=null] A hash of labels to pass to the {{#crossLink "MovieClip/timeline:property"}}{{/crossLink}}
	 * instance associated with this MovieClip. Labels only need to be passed if they need to be used.
	 **/
	function MovieClip(mode, startPosition, loop, labels) {
		this.Container_constructor();
		!MovieClip.inited&&MovieClip.init(); // static init
		
		
	// public properties:
		/**
		 * Controls how this MovieClip advances its time. Must be one of 0 (INDEPENDENT), 1 (SINGLE_FRAME), or 2 (SYNCHED).
		 * See each constant for a description of the behaviour.
		 * @property mode
		 * @type String
		 * @default null
		 **/
		this.mode = mode||MovieClip.INDEPENDENT;
	
		/**
		 * Specifies what the first frame to play in this movieclip, or the only frame to display if mode is SINGLE_FRAME.
		 * @property startPosition
		 * @type Number
		 * @default 0
		 */
		this.startPosition = startPosition || 0;
	
		/**
		 * Indicates whether this MovieClip should loop when it reaches the end of its timeline.
		 * @property loop
		 * @type Boolean
		 * @default true
		 */
		this.loop = loop;
	
		/**
		 * The current frame of the movieclip.
		 * @property currentFrame
		 * @type Number
		 * @default 0
		 * @readonly
		 */
		this.currentFrame = 0;
	
		/**
		 * The TweenJS Timeline that is associated with this MovieClip. This is created automatically when the MovieClip
		 * instance is initialized. Animations are created by adding <a href="http://tweenjs.com">TweenJS</a> Tween
		 * instances to the timeline.
		 *
		 * <h4>Example</h4>
		 *
		 *      var tween = createjs.Tween.get(target).to({x:0}).to({x:100}, 30);
		 *      var mc = new createjs.MovieClip();
		 *      mc.timeline.addTween(tween);
		 *
		 * Elements can be added and removed from the timeline by toggling an "_off" property
		 * using the <code>tweenInstance.to()</code> method. Note that using <code>Tween.set</code> is not recommended to
		 * create MovieClip animations. The following example will toggle the target off on frame 0, and then back on for
		 * frame 1. You can use the "visible" property to achieve the same effect.
		 *
		 *      var tween = createjs.Tween.get(target).to({_off:false})
		 *          .wait(1).to({_off:true})
		 *          .wait(1).to({_off:false});
		 *
		 * @property timeline
		 * @type Timeline
		 * @default null
		 */
		this.timeline = new createjs.Timeline(null, labels, {paused:true, position:startPosition, useTicks:true});
	
		/**
		 * If true, the MovieClip's position will not advance when ticked.
		 * @property paused
		 * @type Boolean
		 * @default false
		 */
		this.paused = false;
	
		/**
		 * If true, actions in this MovieClip's tweens will be run when the playhead advances.
		 * @property actionsEnabled
		 * @type Boolean
		 * @default true
		 */
		this.actionsEnabled = true;
	
		/**
		 * If true, the MovieClip will automatically be reset to its first frame whenever the timeline adds
		 * it back onto the display list. This only applies to MovieClip instances with mode=INDEPENDENT.
		 * <br><br>
		 * For example, if you had a character animation with a "body" child MovieClip instance
		 * with different costumes on each frame, you could set body.autoReset = false, so that
		 * you can manually change the frame it is on, without worrying that it will be reset
		 * automatically.
		 * @property autoReset
		 * @type Boolean
		 * @default true
		 */
		this.autoReset = true;
		
		/**
		 * An array of bounds for each frame in the MovieClip. This is mainly intended for tool output.
		 * @property frameBounds
		 * @type Array
		 * @default null
		 */
		this.frameBounds = this.frameBounds||null; // TODO: Deprecated. This is for backwards support of FlashCC
		
		/**
		 * By default MovieClip instances advance one frame per tick. Specifying a framerate for the MovieClip
		 * will cause it to advance based on elapsed time between ticks as appropriate to maintain the target
		 * framerate.
		 *
		 * For example, if a MovieClip with a framerate of 10 is placed on a Stage being updated at 40fps, then the MovieClip will
		 * advance roughly one frame every 4 ticks. This will not be exact, because the time between each tick will
		 * vary slightly between frames.
		 *
		 * This feature is dependent on the tick event object (or an object with an appropriate "delta" property) being
		 * passed into {{#crossLink "Stage/update"}}{{/crossLink}}.
		 * @property framerate
		 * @type {Number}
		 * @default null
		 **/
		this.framerate = null;
		
		
	// private properties:
		/**
		 * @property _synchOffset
		 * @type Number
		 * @default 0
		 * @private
		 */
		this._synchOffset = 0;
	
		/**
		 * @property _prevPos
		 * @type Number
		 * @default -1
		 * @private
		 */
		this._prevPos = -1; // TODO: evaluate using a ._reset Boolean prop instead of -1.
	
		/**
		 * @property _prevPosition
		 * @type Number
		 * @default 0
		 * @private
		 */
		this._prevPosition = 0;
	
		/**
		 * The time remaining from the previous tick, only applicable when .framerate is set.
		 * @property _t
		 * @type Number
		 * @private
		 */
		this._t = 0;
	
		/**
		 * List of display objects that are actively being managed by the MovieClip.
		 * @property _managed
		 * @type Object
		 * @private
		 */
		this._managed = {};
	}
	var p = createjs.extend(MovieClip, createjs.Container);


// constants:
	/**
	 * The MovieClip will advance independently of its parent, even if its parent is paused.
	 * This is the default mode.
	 * @property INDEPENDENT
	 * @static
	 * @type String
	 * @default "independent"
	 * @readonly
	 **/
	MovieClip.INDEPENDENT = "independent";

	/**
	 * The MovieClip will only display a single frame (as determined by the startPosition property).
	 * @property SINGLE_FRAME
	 * @static
	 * @type String
	 * @default "single"
	 * @readonly
	 **/
	MovieClip.SINGLE_FRAME = "single";

	/**
	 * The MovieClip will be advanced only when its parent advances and will be synched to the position of
	 * the parent MovieClip.
	 * @property SYNCHED
	 * @static
	 * @type String
	 * @default "synched"
	 * @readonly
	 **/
	MovieClip.SYNCHED = "synched";
	
	
// static properties:
	MovieClip.inited = false;
	
	
// static methods:
	MovieClip.init = function() {
		if (MovieClip.inited) { return; }
		// plugins introduce some overhead to Tween, so we only install this if an MC is instantiated.
		MovieClipPlugin.install();
		MovieClip.inited = true;
	};
	
	
// getter / setters:
	/**
	 * Use the {{#crossLink "MovieClip/labels:property"}}{{/crossLink}} property instead.
	 * @method getLabels
	 * @return {Array}
	 * @deprecated
	 **/
	p.getLabels = function() {
		return this.timeline.getLabels();
	};
	
	/**
	 * Use the {{#crossLink "MovieClip/currentLabel:property"}}{{/crossLink}} property instead.
	 * @method getCurrentLabel
	 * @return {String}
	 * @deprecated
	 **/
	p.getCurrentLabel = function() {
		this._updateTimeline();
		return this.timeline.getCurrentLabel();
	};
	
	/**
	 * Use the {{#crossLink "MovieClip/duration:property"}}{{/crossLink}} property instead.
	 * @method getDuration
	 * @return {Number}
	 * @protected
	 **/
	p.getDuration = function() {
		return this.timeline.duration;
	};

	/**
	 * Returns an array of objects with label and position (aka frame) properties, sorted by position.
	 * Shortcut to TweenJS: Timeline.getLabels();
	 * @property labels
	 * @type {Array}
	 * @readonly
	 **/
	
	/**
	 * Returns the name of the label on or immediately before the current frame. See TweenJS: Timeline.getCurrentLabel()
	 * for more information.
	 * @property currentLabel
	 * @type {String}
	 * @readonly
	 **/
	
	/**
	 * Returns the duration of this MovieClip in seconds or ticks. Identical to {{#crossLink "MovieClip/duration:property"}}{{/crossLink}}
	 * and provided for Flash API compatibility.
	 * @property totalFrames
	 * @type {Number}
	 * @readonly
	 **/
	
	/**
	 * Returns the duration of this MovieClip in seconds or ticks.
	 * @property duration
	 * @type {Number}
	 * @readonly
	 **/
	try {
		Object.defineProperties(p, {
			labels: { get: p.getLabels },
			currentLabel: { get: p.getCurrentLabel },
			totalFrames: { get: p.getDuration },
			duration: { get: p.getDuration }
		});
	} catch (e) {}


// public methods:
	/**
	 * Constructor alias for backwards compatibility. This method will be removed in future versions.
	 * Subclasses should be updated to use {{#crossLink "Utility Methods/extends"}}{{/crossLink}}.
	 * @method initialize
	 * @deprecated in favour of `createjs.promote()`
	 **/
	p.initialize = MovieClip; // TODO: Deprecated. This is for backwards support of FlashCC

	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 **/
	p.isVisible = function() {
		// children are placed in draw, so we can't determine if we have content.
		return !!(this.visible && this.alpha > 0 && this.scaleX != 0 && this.scaleY != 0);
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 **/
	p.draw = function(ctx, ignoreCache) {
		// draw to cache first:
		if (this.DisplayObject_draw(ctx, ignoreCache)) { return true; }
		this._updateTimeline();
		this.Container_draw(ctx, ignoreCache);
		return true;
	};
	
	/**
	 * Sets paused to false.
	 * @method play
	 **/
	p.play = function() {
		this.paused = false;
	};
	
	/**
	 * Sets paused to true.
	 * @method stop
	 **/
	p.stop = function() {
		this.paused = true;
	};
	
	/**
	 * Advances this movie clip to the specified position or label and sets paused to false.
	 * @method gotoAndPlay
	 * @param {String|Number} positionOrLabel The animation name or frame number to go to.
	 **/
	p.gotoAndPlay = function(positionOrLabel) {
		this.paused = false;
		this._goto(positionOrLabel);
	};
	
	/**
	 * Advances this movie clip to the specified position or label and sets paused to true.
	 * @method gotoAndStop
	 * @param {String|Number} positionOrLabel The animation or frame name to go to.
	 **/
	p.gotoAndStop = function(positionOrLabel) {
		this.paused = true;
		this._goto(positionOrLabel);
	};
	
	/**
	 * Advances the playhead. This occurs automatically each tick by default.
	 * @param [time] {Number} The amount of time in ms to advance by. Only applicable if framerate is set.
	 * @method advance
	*/
	p.advance = function(time) {
		// TODO: should we worry at all about clips who change their own modes via frame scripts?
		var independent = MovieClip.INDEPENDENT;
		if (this.mode != independent) { return; }
		
		var o=this, fps = o.framerate;
		while ((o = o.parent) && fps == null) {
			if (o.mode == independent) { fps = o._framerate; }
		}
		this._framerate = fps;
		
		var t = (fps != null && fps != -1 && time != null) ? time/(1000/fps) + this._t : 1;
		var frames = t|0;
		this._t = t-frames; // leftover time
		
		while (!this.paused && frames--) {
			this._prevPosition = (this._prevPos < 0) ? 0 : this._prevPosition+1;
			this._updateTimeline();
		}
	};
	
	/**
	 * MovieClip instances cannot be cloned.
	 * @method clone
	 **/
	p.clone = function() {
		// TODO: add support for this? Need to clone the Timeline & retarget tweens - pretty complex.
		throw("MovieClip cannot be cloned.")
	};
	
	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[MovieClip (name="+  this.name +")]";
	};


// private methods:
	/**
	 * @method _tick
	 * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
	 * function.
	 * @protected
	 **/
	p._tick = function(evtObj) {
		this.advance(evtObj&&evtObj.delta);
		this.Container__tick(evtObj);
	};
	
	/**
	 * @method _goto
	 * @param {String|Number} positionOrLabel The animation name or frame number to go to.
	 * @protected
	 **/
	p._goto = function(positionOrLabel) {
		var pos = this.timeline.resolve(positionOrLabel);
		if (pos == null) { return; }
		// prevent _updateTimeline from overwriting the new position because of a reset:
		if (this._prevPos == -1) { this._prevPos = NaN; }
		this._prevPosition = pos;
		this._t = 0;
		this._updateTimeline();
	};
	
	/**
	 * @method _reset
	 * @private
	 **/
	p._reset = function() {
		this._prevPos = -1;
		this._t = this.currentFrame = 0;
		this.paused = false;
	};
	
	/**
	 * @method _updateTimeline
	 * @protected
	 **/
	p._updateTimeline = function() {
		var tl = this.timeline;
		var synched = this.mode != MovieClip.INDEPENDENT;
		tl.loop = (this.loop==null) ? true : this.loop;
		
		var pos = synched ? this.startPosition + (this.mode==MovieClip.SINGLE_FRAME?0:this._synchOffset) : (this._prevPos < 0 ? 0 : this._prevPosition);
		var mode = synched || !this.actionsEnabled ? createjs.Tween.NONE : null;
		
		// pre-assign currentFrame so it is available to frame scripts:
		this.currentFrame = tl._calcPosition(pos);
		
		// update timeline position, ignoring actions if this is a graphic.
		tl.setPosition(pos, mode);

		this._prevPosition = tl._prevPosition;
		if (this._prevPos == tl._prevPos) { return; }
		this.currentFrame = this._prevPos = tl._prevPos;

		for (var n in this._managed) { this._managed[n] = 1; }

		var tweens = tl._tweens;
		for (var i=0, l=tweens.length; i<l; i++) {
			var tween = tweens[i];
			var target = tween._target;
			if (target == this || tween.passive) { continue; } // TODO: this assumes actions tween has this as the target. Valid?
			var offset = tween._stepPosition;

			if (target instanceof createjs.DisplayObject) {
				// motion tween.
				this._addManagedChild(target, offset);
			} else {
				// state tween.
				this._setState(target.state, offset);
			}
		}

		var kids = this.children;
		for (i=kids.length-1; i>=0; i--) {
			var id = kids[i].id;
			if (this._managed[id] == 1) {
				this.removeChildAt(i);
				delete(this._managed[id]);
			}
		}
	};

	/**
	 * @method _setState
	 * @param {Array} state
	 * @param {Number} offset
	 * @protected
	 **/
	p._setState = function(state, offset) {
		if (!state) { return; }
		for (var i=state.length-1;i>=0;i--) {
			var o = state[i];
			var target = o.t;
			var props = o.p;
			for (var n in props) { target[n] = props[n]; }
			this._addManagedChild(target, offset);
		}
	};

	/**
	 * Adds a child to the timeline, and sets it up as a managed child.
	 * @method _addManagedChild
	 * @param {MovieClip} child The child MovieClip to manage
	 * @param {Number} offset
	 * @private
	 **/
	p._addManagedChild = function(child, offset) {
		if (child._off) { return; }
		this.addChildAt(child,0);

		if (child instanceof MovieClip) {
			child._synchOffset = offset;
			// TODO: this does not precisely match Flash. Flash loses track of the clip if it is renamed or removed from the timeline, which causes it to reset.
			if (child.mode == MovieClip.INDEPENDENT && child.autoReset && !this._managed[child.id]) { child._reset(); }
		}
		this._managed[child.id] = 2;
	};
	
	/**
	 * @method _getBounds
	 * @param {Matrix2D} matrix
	 * @param {Boolean} ignoreTransform
	 * @return {Rectangle}
	 * @protected
	 **/
	p._getBounds = function(matrix, ignoreTransform) {
		var bounds = this.DisplayObject_getBounds();
		if (!bounds) {
			this._updateTimeline();
			if (this.frameBounds) { bounds = this._rectangle.copy(this.frameBounds[this.currentFrame]); }
		}
		if (bounds) { return this._transformBounds(bounds, matrix, ignoreTransform); }
		return this.Container__getBounds(matrix, ignoreTransform);
	};


	createjs.MovieClip = createjs.promote(MovieClip, "Container");



// MovieClipPlugin for TweenJS:
	/**
	 * This plugin works with <a href="http://tweenjs.com" target="_blank">TweenJS</a> to prevent the startPosition
	 * property from tweening.
	 * @private
	 * @class MovieClipPlugin
	 * @constructor
	 **/
	function MovieClipPlugin() {
		throw("MovieClipPlugin cannot be instantiated.")
	}
	
	/**
	 * @method priority
	 * @private
	 **/
	MovieClipPlugin.priority = 100; // very high priority, should run first

	/**
	 * @method install
	 * @private
	 **/
	MovieClipPlugin.install = function() {
		createjs.Tween.installPlugin(MovieClipPlugin, ["startPosition"]);
	};
	
	/**
	 * @method init
	 * @param {Tween} tween
	 * @param {String} prop
	 * @param {String|Number|Boolean} value
	 * @private
	 **/
	MovieClipPlugin.init = function(tween, prop, value) {
		return value;
	};
	
	/**
	 * @method step
	 * @private
	 **/
	MovieClipPlugin.step = function() {
		// unused.
	};

	/**
	 * @method tween
	 * @param {Tween} tween
	 * @param {String} prop
	 * @param {String | Number | Boolean} value
	 * @param {Array} startValues
	 * @param {Array} endValues
	 * @param {Number} ratio
	 * @param {Object} wait
	 * @param {Object} end
	 * @return {*}
	 */
	MovieClipPlugin.tween = function(tween, prop, value, startValues, endValues, ratio, wait, end) {
		if (!(tween.target instanceof MovieClip)) { return value; }
		return (ratio == 1 ? endValues[prop] : startValues[prop]);
	};

}());

//##############################################################################
// SpriteSheetUtils.js
//##############################################################################

(function() {
	"use strict";
	
	
// constructor:
	/**
	 * The SpriteSheetUtils class is a collection of static methods for working with {{#crossLink "SpriteSheet"}}{{/crossLink}}s.
	 * A sprite sheet is a series of images (usually animation frames) combined into a single image on a regular grid. For
	 * example, an animation consisting of 8 100x100 images could be combined into a 400x200 sprite sheet (4 frames across
	 * by 2 high). The SpriteSheetUtils class uses a static interface and should not be instantiated.
	 * @class SpriteSheetUtils
	 * @static
	 **/
	function SpriteSheetUtils() {
		throw "SpriteSheetUtils cannot be instantiated";
	}


// private static properties:
	/**
	 * @property _workingCanvas
	 * @static
	 * @type HTMLCanvasElement | Object
	 * @protected
	*/
	/**
	 * @property _workingContext
	 * @static
	 * @type CanvasRenderingContext2D
	 * @protected
	*/
	var canvas = (createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"));
	if (canvas.getContext) {
		SpriteSheetUtils._workingCanvas = canvas;
		SpriteSheetUtils._workingContext = canvas.getContext("2d");
		canvas.width = canvas.height = 1;
	}


// public static methods:
	/**
	 * <b>This is an experimental method, and may be buggy. Please report issues.</b><br/><br/>
	 * Extends the existing sprite sheet by flipping the original frames horizontally, vertically, or both,
	 * and adding appropriate animation & frame data. The flipped animations will have a suffix added to their names
	 * (_h, _v, _hv as appropriate). Make sure the sprite sheet images are fully loaded before using this method.
	 * <br/><br/>
	 * For example:<br/>
	 * SpriteSheetUtils.addFlippedFrames(mySpriteSheet, true, true);
	 * The above would add frames that are flipped horizontally AND frames that are flipped vertically.
	 * <br/><br/>
	 * Note that you can also flip any display object by setting its scaleX or scaleY to a negative value. On some
	 * browsers (especially those without hardware accelerated canvas) this can result in slightly degraded performance,
	 * which is why addFlippedFrames is available.
	 * @method addFlippedFrames
	 * @static
	 * @param {SpriteSheet} spriteSheet
	 * @param {Boolean} horizontal If true, horizontally flipped frames will be added.
	 * @param {Boolean} vertical If true, vertically flipped frames will be added.
	 * @param {Boolean} both If true, frames that are flipped both horizontally and vertically will be added.
	 * @deprecated Modern browsers perform better when flipping via a transform (ex. scaleX=-1) rendering this obsolete.
	 **/
	SpriteSheetUtils.addFlippedFrames = function(spriteSheet, horizontal, vertical, both) {
		if (!horizontal && !vertical && !both) { return; }

		var count = 0;
		if (horizontal) { SpriteSheetUtils._flip(spriteSheet,++count,true,false); }
		if (vertical) { SpriteSheetUtils._flip(spriteSheet,++count,false,true); }
		if (both) { SpriteSheetUtils._flip(spriteSheet,++count,true,true); }
	};

	/**
	 * Returns a single frame of the specified sprite sheet as a new PNG image. An example of when this may be useful is
	 * to use a spritesheet frame as the source for a bitmap fill.
	 *
	 * <strong>WARNING:</strong> In almost all cases it is better to display a single frame using a {{#crossLink "Sprite"}}{{/crossLink}}
	 * with a {{#crossLink "Sprite/gotoAndStop"}}{{/crossLink}} call than it is to slice out a frame using this
	 * method and display it with a Bitmap instance. You can also crop an image using the {{#crossLink "Bitmap/sourceRect"}}{{/crossLink}}
	 * property of {{#crossLink "Bitmap"}}{{/crossLink}}.
	 *
	 * The extractFrame method may cause cross-domain warnings since it accesses pixels directly on the canvas.
	 * @method extractFrame
	 * @static
	 * @param {SpriteSheet} spriteSheet The SpriteSheet instance to extract a frame from.
	 * @param {Number|String} frameOrAnimation The frame number or animation name to extract. If an animation
	 * name is specified, only the first frame of the animation will be extracted.
	 * @return {HTMLImageElement} a single frame of the specified sprite sheet as a new PNG image.
	*/
	SpriteSheetUtils.extractFrame = function(spriteSheet, frameOrAnimation) {
		if (isNaN(frameOrAnimation)) {
			frameOrAnimation = spriteSheet.getAnimation(frameOrAnimation).frames[0];
		}
		var data = spriteSheet.getFrame(frameOrAnimation);
		if (!data) { return null; }
		var r = data.rect;
		var canvas = SpriteSheetUtils._workingCanvas;
		canvas.width = r.width;
		canvas.height = r.height;
		SpriteSheetUtils._workingContext.drawImage(data.image, r.x, r.y, r.width, r.height, 0, 0, r.width, r.height);
		var img = document.createElement("img");
		img.src = canvas.toDataURL("image/png");
		return img;
	};

	/**
	 * Merges the rgb channels of one image with the alpha channel of another. This can be used to combine a compressed
	 * JPEG image containing color data with a PNG32 monochromatic image containing alpha data. With certain types of
	 * images (those with detail that lend itself to JPEG compression) this can provide significant file size savings
	 * versus a single RGBA PNG32. This method is very fast (generally on the order of 1-2 ms to run).
	 * @method mergeAlpha
	 * @static
	 * @param {HTMLImageElement} rbgImage The image (or canvas) containing the RGB channels to use.
	 * @param {HTMLImageElement} alphaImage The image (or canvas) containing the alpha channel to use.
	 * @param {HTMLCanvasElement} canvas Optional. If specified, this canvas will be used and returned. If not, a new canvas will be created.
	 * @return {HTMLCanvasElement} A canvas with the combined image data. This can be used as a source for Bitmap or SpriteSheet.
	 * @deprecated Tools such as ImageAlpha generally provide better results. This will be moved to sandbox in the future.
	*/
	SpriteSheetUtils.mergeAlpha = function(rgbImage, alphaImage, canvas) {
		if (!canvas) { canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas"); }
		canvas.width = Math.max(alphaImage.width, rgbImage.width);
		canvas.height = Math.max(alphaImage.height, rgbImage.height);
		var ctx = canvas.getContext("2d");
		ctx.save();
		ctx.drawImage(rgbImage,0,0);
		ctx.globalCompositeOperation = "destination-in";
		ctx.drawImage(alphaImage,0,0);
		ctx.restore();
		return canvas;
	};


// private static methods:
	SpriteSheetUtils._flip = function(spriteSheet, count, h, v) {
		var imgs = spriteSheet._images;
		var canvas = SpriteSheetUtils._workingCanvas;
		var ctx = SpriteSheetUtils._workingContext;
		var il = imgs.length/count;
		for (var i=0;i<il;i++) {
			var src = imgs[i];
			src.__tmp = i; // a bit hacky, but faster than doing indexOf below.
			ctx.setTransform(1,0,0,1,0,0);
			ctx.clearRect(0,0,canvas.width+1,canvas.height+1);
			canvas.width = src.width;
			canvas.height = src.height;
			ctx.setTransform(h?-1:1, 0, 0, v?-1:1, h?src.width:0, v?src.height:0);
			ctx.drawImage(src,0,0);
			var img = document.createElement("img");
			img.src = canvas.toDataURL("image/png");
			// work around a strange bug in Safari:
			img.width = src.width;
			img.height = src.height;
			imgs.push(img);
		}

		var frames = spriteSheet._frames;
		var fl = frames.length/count;
		for (i=0;i<fl;i++) {
			src = frames[i];
			var rect = src.rect.clone();
			img = imgs[src.image.__tmp+il*count];

			var frame = {image:img,rect:rect,regX:src.regX,regY:src.regY};
			if (h) {
				rect.x = img.width-rect.x-rect.width; // update rect
				frame.regX = rect.width-src.regX; // update registration point
			}
			if (v) {
				rect.y = img.height-rect.y-rect.height;  // update rect
				frame.regY = rect.height-src.regY; // update registration point
			}
			frames.push(frame);
		}

		var sfx = "_"+(h?"h":"")+(v?"v":"");
		var names = spriteSheet._animations;
		var data = spriteSheet._data;
		var al = names.length/count;
		for (i=0;i<al;i++) {
			var name = names[i];
			src = data[name];
			var anim = {name:name+sfx,speed:src.speed,next:src.next,frames:[]};
			if (src.next) { anim.next += sfx; }
			frames = src.frames;
			for (var j=0,l=frames.length;j<l;j++) {
				anim.frames.push(frames[j]+fl*count);
			}
			data[anim.name] = anim;
			names.push(anim.name);
		}
	};


	createjs.SpriteSheetUtils = SpriteSheetUtils;
}());

//##############################################################################
// SpriteSheetBuilder.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * The SpriteSheetBuilder allows you to generate {{#crossLink "SpriteSheet"}}{{/crossLink}} instances at run time
	 * from any display object. This can allow you to maintain your assets as vector graphics (for low file size), and
	 * render them at run time as SpriteSheets for better performance.
	 *
	 * SpriteSheets can be built either synchronously, or asynchronously, so that large SpriteSheets can be generated
	 * without locking the UI.
	 *
	 * Note that the "images" used in the generated SpriteSheet are actually canvas elements, and that they will be
	 * sized to the nearest power of 2 up to the value of {{#crossLink "SpriteSheetBuilder/maxWidth:property"}}{{/crossLink}}
	 * or {{#crossLink "SpriteSheetBuilder/maxHeight:property"}}{{/crossLink}}.
	 * @class SpriteSheetBuilder
	 * @param {Number} [framerate=0] The {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} of
	 * {{#crossLink "SpriteSheet"}}{{/crossLink}} instances that are created.
	 * @extends EventDispatcher
	 * @constructor
	 **/
	function SpriteSheetBuilder(framerate) {
		this.EventDispatcher_constructor();
		
	// public properties:
		/**
		 * The maximum width for the images (not individual frames) in the generated SpriteSheet. It is recommended to
		 * use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max
		 * dimensions, then additional images will be created as needed.
		 * @property maxWidth
		 * @type Number
		 * @default 2048
		*/
		this.maxWidth = 2048;
	
		/**
		 * The maximum height for the images (not individual frames) in the generated SpriteSheet. It is recommended to
		 * use a power of 2 for this value (ex. 1024, 2048, 4096). If the frames cannot all fit within the max
		 * dimensions, then additional images will be created as needed.
		 * @property maxHeight
		 * @type Number
		 * @default 2048
		 **/
		this.maxHeight = 2048;
	
		/**
		 * The SpriteSheet that was generated. This will be null before a build is completed successfully.
		 * @property spriteSheet
		 * @type SpriteSheet
		 **/
		this.spriteSheet = null;
	
		/**
		 * The scale to apply when drawing all frames to the SpriteSheet. This is multiplied against any scale specified
		 * in the addFrame call. This can be used, for example, to generate a SpriteSheet at run time that is tailored
		 * to the a specific device resolution (ex. tablet vs mobile).
		 * @property scale
		 * @type Number
		 * @default 1
		 **/
		this.scale = 1;
	
		/**
		* The padding to use between frames. This is helpful to preserve antialiasing on drawn vector content.
		* @property padding
		* @type Number
		* @default 1
		**/
		this.padding = 1;
	
		/**
		 * A number from 0.01 to 0.99 that indicates what percentage of time the builder can use. This can be
		 * thought of as the number of seconds per second the builder will use. For example, with a timeSlice value of 0.3,
		 * the builder will run 20 times per second, using approximately 15ms per build (30% of available time, or 0.3s per second).
		 * Defaults to 0.3.
		 * @property timeSlice
		 * @type Number
		 * @default 0.3
		 **/
		this.timeSlice = 0.3;
	
		/**
		 * A value between 0 and 1 that indicates the progress of a build, or -1 if a build has not
		 * been initiated.
		 * @property progress
		 * @type Number
		 * @default -1
		 * @readonly
		 */
		this.progress = -1;

		/**
		 * A {{#crossLink "SpriteSheet/framerate:property"}}{{/crossLink}} value that will be passed to new {{#crossLink "SpriteSheet"}}{{/crossLink}} instances that are
		 * created. If no framerate is specified (or it is 0), then SpriteSheets will use the {{#crossLink "Ticker"}}{{/crossLink}}
		 * framerate.
		 * @property framerate
		 * @type Number
		 * @default 0
		 */
		this.framerate = framerate || 0;
	
	
	// private properties:
		/**
		 * @property _frames
		 * @protected
		 * @type Array
		 **/
		this._frames = [];
	
		/**
		 * @property _animations
		 * @protected
		 * @type Array
		 **/
		this._animations = {};
	
		/**
		 * @property _data
		 * @protected
		 * @type Array
		 **/
		this._data = null;
	
		/**
		 * @property _nextFrameIndex
		 * @protected
		 * @type Number
		 **/
		this._nextFrameIndex = 0;
	
		/**
		 * @property _index
		 * @protected
		 * @type Number
		 **/
		this._index = 0;
	
		/**
		 * @property _timerID
		 * @protected
		 * @type Number
		 **/
		this._timerID = null;
	
		/**
		 * @property _scale
		 * @protected
		 * @type Number
		 **/
		this._scale = 1;
	}
	var p = createjs.extend(SpriteSheetBuilder, createjs.EventDispatcher);

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// constants:
	SpriteSheetBuilder.ERR_DIMENSIONS = "frame dimensions exceed max spritesheet dimensions";
	SpriteSheetBuilder.ERR_RUNNING = "a build is already running";

// events:
	/**
	 * Dispatched when a build completes.
	 * @event complete
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @since 0.6.0
	 */

	/**
	 * Dispatched when an asynchronous build has progress.
	 * @event progress
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {Number} progress The current progress value (0-1).
	 * @since 0.6.0
	 */


// public methods:
	/**
	 * Adds a frame to the {{#crossLink "SpriteSheet"}}{{/crossLink}}. Note that the frame will not be drawn until you
	 * call {{#crossLink "SpriteSheetBuilder/build"}}{{/crossLink}} method. The optional setup params allow you to have
	 * a function run immediately before the draw occurs. For example, this allows you to add a single source multiple
	 * times, but manipulate it or its children to change it to generate different frames.
	 *
	 * Note that the source's transformations (x, y, scale, rotate, alpha) will be ignored, except for regX/Y. To apply
	 * transforms to a source object and have them captured in the SpriteSheet, simply place it into a {{#crossLink "Container"}}{{/crossLink}}
	 * and pass in the Container as the source.
	 * @method addFrame
	 * @param {DisplayObject} source The source {{#crossLink "DisplayObject"}}{{/crossLink}}  to draw as the frame.
	 * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the
	 * source to draw to the frame. If not specified, it will look for a `getBounds` method, bounds property, or
	 * `nominalBounds` property on the source to use. If one is not found, the frame will be skipped.
	 * @param {Number} [scale=1] Optional. The scale to draw this frame at. Default is 1.
	 * @param {Function} [setupFunction] A function to call immediately before drawing this frame. It will be called with two parameters: the source, and setupData.
	 * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
	 * @return {Number} The index of the frame that was just added, or null if a sourceRect could not be determined.
	 **/
	p.addFrame = function(source, sourceRect, scale, setupFunction, setupData) {
		if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
		var rect = sourceRect||source.bounds||source.nominalBounds;
		if (!rect&&source.getBounds) { rect = source.getBounds(); }
		if (!rect) { return null; }
		scale = scale||1;
		return this._frames.push({source:source, sourceRect:rect, scale:scale, funct:setupFunction, data:setupData, index:this._frames.length, height:rect.height*scale})-1;
	};

	/**
	 * Adds an animation that will be included in the created {{#crossLink "SpriteSheet"}}{{/crossLink}}.
	 * @method addAnimation
	 * @param {String} name The name for the animation.
	 * @param {Array} frames An array of frame indexes that comprise the animation. Ex. [3,6,5] would describe an animation
	 * that played frame indexes 3, 6, and 5 in that order.
	 * @param {String} [next] Specifies the name of the animation to continue to after this animation ends. You can
	 * also pass false to have the animation stop when it ends. By default it will loop to the start of the same animation.
	 * @param {Number} [speed] Specifies a frame advance speed for this animation. For example, a value of 0.5 would
	 * cause the animation to advance every second tick. Note that earlier versions used `frequency` instead, which had
	 * the opposite effect.
	 **/
	p.addAnimation = function(name, frames, next, speed) {
		if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
		this._animations[name] = {frames:frames, next:next, speed:speed};
	};

	/**
	 * This will take a {{#crossLink "MovieClip"}}{{/crossLink}} instance, and add its frames and labels to this
	 * builder. Labels will be added as an animation running from the label index to the next label. For example, if
	 * there is a label named "foo" at frame 0 and a label named "bar" at frame 10, in a MovieClip with 15 frames, it
	 * will add an animation named "foo" that runs from frame index 0 to 9, and an animation named "bar" that runs from
	 * frame index 10 to 14.
	 *
	 * Note that this will iterate through the full MovieClip with {{#crossLink "MovieClip/actionsEnabled:property"}}{{/crossLink}}
	 * set to `false`, ending on the last frame.
	 * @method addMovieClip
	 * @param {MovieClip} source The source MovieClip instance to add to the SpriteSheet.
	 * @param {Rectangle} [sourceRect] A {{#crossLink "Rectangle"}}{{/crossLink}} defining the portion of the source to
	 * draw to the frame. If not specified, it will look for a {{#crossLink "DisplayObject/getBounds"}}{{/crossLink}}
	 * method, `frameBounds` Array, `bounds` property, or `nominalBounds` property on the source to use. If one is not
	 * found, the MovieClip will be skipped.
	 * @param {Number} [scale=1] The scale to draw the movie clip at.
	 * @param {Function} [setupFunction] A function to call immediately before drawing each frame. It will be called
	 * with three parameters: the source, setupData, and the frame index.
	 * @param {Object} [setupData] Arbitrary setup data to pass to setupFunction as the second parameter.
	 * @param {Function} [labelFunction] This method will be called for each MovieClip label that is added with four
	 * parameters: the label name, the source MovieClip instance, the starting frame index (in the movieclip timeline)
	 * and the end index. It must return a new name for the label/animation, or `false` to exclude the label.
	 **/
	p.addMovieClip = function(source, sourceRect, scale, setupFunction, setupData, labelFunction) {
		if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
		var rects = source.frameBounds;
		var rect = sourceRect||source.bounds||source.nominalBounds;
		if (!rect&&source.getBounds) { rect = source.getBounds(); }
		if (!rect && !rects) { return; }

		var i, l, baseFrameIndex = this._frames.length;
		var duration = source.timeline.duration;
		for (i=0; i<duration; i++) {
			var r = (rects&&rects[i]) ? rects[i] : rect;
			this.addFrame(source, r, scale, this._setupMovieClipFrame, {i:i, f:setupFunction, d:setupData});
		}
		var labels = source.timeline._labels;
		var lbls = [];
		for (var n in labels) {
			lbls.push({index:labels[n], label:n});
		}
		if (lbls.length) {
			lbls.sort(function(a,b){ return a.index-b.index; });
			for (i=0,l=lbls.length; i<l; i++) {
				var label = lbls[i].label;
				var start = baseFrameIndex+lbls[i].index;
				var end = baseFrameIndex+((i == l-1) ? duration : lbls[i+1].index);
				var frames = [];
				for (var j=start; j<end; j++) { frames.push(j); }
				if (labelFunction) {
					label = labelFunction(label, source, start, end);
					if (!label) { continue; }
				}
				this.addAnimation(label, frames, true); // for now, this loops all animations.
			}
		}
	};

	/**
	 * Builds a {{#crossLink "SpriteSheet"}}{{/crossLink}} instance based on the current frames.
	 * @method build
	 * @return {SpriteSheet} The created SpriteSheet instance, or null if a build is already running or an error
	 * occurred.
	 **/
	p.build = function() {
		if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
		this._startBuild();
		while (this._drawNext()) {}
		this._endBuild();
		return this.spriteSheet;
	};

	/**
	 * Asynchronously builds a {{#crossLink "SpriteSheet"}}{{/crossLink}} instance based on the current frames. It will
	 * run 20 times per second, using an amount of time defined by `timeSlice`. When it is complete it will call the
	 * specified callback.
	 * @method buildAsync
	 * @param {Number} [timeSlice] Sets the timeSlice property on this instance.
	 **/
	p.buildAsync = function(timeSlice) {
		if (this._data) { throw SpriteSheetBuilder.ERR_RUNNING; }
		this.timeSlice = timeSlice;
		this._startBuild();
		var _this = this;
		this._timerID = setTimeout(function() { _this._run(); }, 50-Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50);
	};

	/**
	 * Stops the current asynchronous build.
	 * @method stopAsync
	 **/
	p.stopAsync = function() {
		clearTimeout(this._timerID);
		this._data = null;
	};

	/**
	 * SpriteSheetBuilder instances cannot be cloned.
	 * @method clone
	 **/
	p.clone = function() {
		throw("SpriteSheetBuilder cannot be cloned.");
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[SpriteSheetBuilder]";
	};


// private methods:
	/**
	 * @method _startBuild
	 * @protected
	 **/
	p._startBuild = function() {
		var pad = this.padding||0;
		this.progress = 0;
		this.spriteSheet = null;
		this._index = 0;
		this._scale = this.scale;
		var dataFrames = [];
		this._data = {
			images: [],
			frames: dataFrames,
			framerate: this.framerate,
			animations: this._animations // TODO: should we "clone" _animations in case someone adds more animations after a build?
		};

		var frames = this._frames.slice();
		frames.sort(function(a,b) { return (a.height<=b.height) ? -1 : 1; });

		if (frames[frames.length-1].height+pad*2 > this.maxHeight) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
		var y=0, x=0;
		var img = 0;
		while (frames.length) {
			var o = this._fillRow(frames, y, img, dataFrames, pad);
			if (o.w > x) { x = o.w; }
			y += o.h;
			if (!o.h || !frames.length) {
				var canvas = createjs.createCanvas?createjs.createCanvas():document.createElement("canvas");
				canvas.width = this._getSize(x,this.maxWidth);
				canvas.height = this._getSize(y,this.maxHeight);
				this._data.images[img] = canvas;
				if (!o.h) {
					x=y=0;
					img++;
				}
			}
		}
	};
	
	/**
	 * @method _setupMovieClipFrame
	 * @protected
	 * @return {Number} The width & height of the row.
	 **/
	p._setupMovieClipFrame = function(source, data) {
		var ae = source.actionsEnabled;
		source.actionsEnabled = false;
		source.gotoAndStop(data.i);
		source.actionsEnabled = ae;
		data.f&&data.f(source, data.d, data.i);
	};

	/**
	 * @method _getSize
	 * @protected
	 * @return {Number} The width & height of the row.
	 **/
	p._getSize = function(size,max) {
		var pow = 4;
		while (Math.pow(2,++pow) < size){}
		return Math.min(max,Math.pow(2,pow));
	};

	/**
	 * @method _fillRow
	 * @param {Array} frames
	 * @param {Number} y
	 * @param {HTMLImageElement} img
	 * @param {Object} dataFrames
	 * @param {Number} pad
	 * @protected
	 * @return {Number} The width & height of the row.
	 **/
	p._fillRow = function(frames, y, img, dataFrames, pad) {
		var w = this.maxWidth;
		var maxH = this.maxHeight;
		y += pad;
		var h = maxH-y;
		var x = pad;
		var height = 0;
		for (var i=frames.length-1; i>=0; i--) {
			var frame = frames[i];
			var sc = this._scale*frame.scale;
			var rect = frame.sourceRect;
			var source = frame.source;
			var rx = Math.floor(sc*rect.x-pad);
			var ry = Math.floor(sc*rect.y-pad);
			var rh = Math.ceil(sc*rect.height+pad*2);
			var rw = Math.ceil(sc*rect.width+pad*2);
			if (rw > w) { throw SpriteSheetBuilder.ERR_DIMENSIONS; }
			if (rh > h || x+rw > w) { continue; }
			frame.img = img;
			frame.rect = new createjs.Rectangle(x,y,rw,rh);
			height = height || rh;
			frames.splice(i,1);
			dataFrames[frame.index] = [x,y,rw,rh,img,Math.round(-rx+sc*source.regX-pad),Math.round(-ry+sc*source.regY-pad)];
			x += rw;
		}
		return {w:x, h:height};
	};

	/**
	 * @method _endBuild
	 * @protected
	 **/
	p._endBuild = function() {
		this.spriteSheet = new createjs.SpriteSheet(this._data);
		this._data = null;
		this.progress = 1;
		this.dispatchEvent("complete");
	};

	/**
	 * @method _run
	 * @protected
	 **/
	p._run = function() {
		var ts = Math.max(0.01, Math.min(0.99, this.timeSlice||0.3))*50;
		var t = (new Date()).getTime()+ts;
		var complete = false;
		while (t > (new Date()).getTime()) {
			if (!this._drawNext()) { complete = true; break; }
		}
		if (complete) {
			this._endBuild();
		} else {
			var _this = this;
			this._timerID = setTimeout(function() { _this._run(); }, 50-ts);
		}
		var p = this.progress = this._index/this._frames.length;
		if (this.hasEventListener("progress")) {
			var evt = new createjs.Event("progress");
			evt.progress = p;
			this.dispatchEvent(evt);
		}
	};

	/**
	 * @method _drawNext
	 * @protected
	 * @return Boolean Returns false if this is the last draw.
	 **/
	p._drawNext = function() {
		var frame = this._frames[this._index];
		var sc = frame.scale*this._scale;
		var rect = frame.rect;
		var sourceRect = frame.sourceRect;
		var canvas = this._data.images[frame.img];
		var ctx = canvas.getContext("2d");
		frame.funct&&frame.funct(frame.source, frame.data);
		ctx.save();
		ctx.beginPath();
		ctx.rect(rect.x, rect.y, rect.width, rect.height);
		ctx.clip();
		ctx.translate(Math.ceil(rect.x-sourceRect.x*sc), Math.ceil(rect.y-sourceRect.y*sc));
		ctx.scale(sc,sc);
		frame.source.draw(ctx); // display object will draw itself.
		ctx.restore();
		return (++this._index) < this._frames.length;
	};


	createjs.SpriteSheetBuilder = createjs.promote(SpriteSheetBuilder, "EventDispatcher");
}());

//##############################################################################
// DOMElement.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * <b>This class is still experimental, and more advanced use is likely to be buggy. Please report bugs.</b>
	 *
	 * A DOMElement allows you to associate a HTMLElement with the display list. It will be transformed
	 * within the DOM as though it is child of the {{#crossLink "Container"}}{{/crossLink}} it is added to. However, it is
	 * not rendered to canvas, and as such will retain whatever z-index it has relative to the canvas (ie. it will be
	 * drawn in front of or behind the canvas).
	 *
	 * The position of a DOMElement is relative to their parent node in the DOM. It is recommended that
	 * the DOM Object be added to a div that also contains the canvas so that they share the same position
	 * on the page.
	 *
	 * DOMElement is useful for positioning HTML elements over top of canvas content, and for elements
	 * that you want to display outside the bounds of the canvas. For example, a tooltip with rich HTML
	 * content.
	 *
	 * <h4>Mouse Interaction</h4>
	 *
	 * DOMElement instances are not full EaselJS display objects, and do not participate in EaselJS mouse
	 * events or support methods like hitTest. To get mouse events from a DOMElement, you must instead add handlers to
	 * the htmlElement (note, this does not support EventDispatcher)
	 *
	 *      var domElement = new createjs.DOMElement(htmlElement);
	 *      domElement.htmlElement.onclick = function() {
	 *          console.log("clicked");
	 *      }
	 *
	 * @class DOMElement
	 * @extends DisplayObject
	 * @constructor
	 * @param {HTMLElement} htmlElement A reference or id for the DOM element to manage.
	 */
	function DOMElement(htmlElement) {
		this.DisplayObject_constructor();
		
		if (typeof(htmlElement)=="string") { htmlElement = document.getElementById(htmlElement); }
		this.mouseEnabled = false;
		
		var style = htmlElement.style;
		style.position = "absolute";
		style.transformOrigin = style.WebkitTransformOrigin = style.msTransformOrigin = style.MozTransformOrigin = style.OTransformOrigin = "0% 0%";
		
		
	// public properties:
		/**
		 * The DOM object to manage.
		 * @property htmlElement
		 * @type HTMLElement
		 */
		this.htmlElement = htmlElement;
	
	
	// private properties:
		/**
		 * @property _oldMtx
		 * @type Matrix2D
		 * @protected
		 */
		this._oldProps = null;
	}
	var p = createjs.extend(DOMElement, createjs.DisplayObject);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// public methods:
	/**
	 * Returns true or false indicating whether the display object would be visible if drawn to a canvas.
	 * This does not account for whether it would be visible within the boundaries of the stage.
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method isVisible
	 * @return {Boolean} Boolean indicating whether the display object would be visible if drawn to a canvas
	 */
	p.isVisible = function() {
		return this.htmlElement != null;
	};

	/**
	 * Draws the display object into the specified context ignoring its visible, alpha, shadow, and transform.
	 * Returns true if the draw was handled (useful for overriding functionality).
	 * NOTE: This method is mainly for internal use, though it may be useful for advanced uses.
	 * @method draw
	 * @param {CanvasRenderingContext2D} ctx The canvas 2D context object to draw into.
	 * @param {Boolean} ignoreCache Indicates whether the draw operation should ignore any current cache.
	 * For example, used for drawing the cache (to prevent it from simply drawing an existing cache back
	 * into itself).
	 * @return {Boolean}
	 */
	p.draw = function(ctx, ignoreCache) {
		// this relies on the _tick method because draw isn't called if the parent is not visible.
		// the actual update happens in _handleDrawEnd
		return true;
	};

	/**
	 * Not applicable to DOMElement.
	 * @method cache
	 */
	p.cache = function() {};

	/**
	 * Not applicable to DOMElement.
	 * @method uncache
	 */
	p.uncache = function() {};

	/**
	 * Not applicable to DOMElement.
	 * @method updateCache
	 */
	p.updateCache = function() {};

	/**
	 * Not applicable to DOMElement.
	 * @method hitTest
	 */
	p.hitTest = function() {};

	/**
	 * Not applicable to DOMElement.
	 * @method localToGlobal
	 */
	p.localToGlobal = function() {};

	/**
	 * Not applicable to DOMElement.
	 * @method globalToLocal
	 */
	p.globalToLocal = function() {};

	/**
	 * Not applicable to DOMElement.
	 * @method localToLocal
	 */
	p.localToLocal = function() {};

	/**
	 * DOMElement cannot be cloned. Throws an error.
	 * @method clone
	 */
	p.clone = function() {
		throw("DOMElement cannot be cloned.")
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 */
	p.toString = function() {
		return "[DOMElement (name="+  this.name +")]";
	};

	/**
     * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
	 * are not full EaselJS display objects and do not participate in EaselJS mouse events.
	 * @event click
	 */

     /**
     * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
 	 * are not full EaselJS display objects and do not participate in EaselJS mouse events.
	 * @event dblClick
	 */

     /**
      * Interaction events should be added to `htmlElement`, and not the DOMElement instance, since DOMElement instances
 	  * are not full EaselJS display objects and do not participate in EaselJS mouse events.
	  * @event mousedown
	  */

     /**
      * The HTMLElement can listen for the mouseover event, not the DOMElement instance.
      * Since DOMElement instances are not full EaselJS display objects and do not participate in EaselJS mouse events.
      * @event mouseover
	  */

     /**
      * Not applicable to DOMElement.
	  * @event tick
	  */


// private methods:
	/**
	 * @method _tick
	 * @param {Object} evtObj An event object that will be dispatched to all tick listeners. This object is reused between dispatchers to reduce construction & GC costs.
	 * function.
	 * @protected
	 */
	p._tick = function(evtObj) {
		var stage = this.getStage();
		stage&&stage.on("drawend", this._handleDrawEnd, this, true);
		this.DisplayObject__tick(evtObj);
	};
	
	/**
	 * @method _handleDrawEnd
	 * @param {Event} evt
	 * @protected
	 */
	p._handleDrawEnd = function(evt) {
		var o = this.htmlElement;
		if (!o) { return; }
		var style = o.style;
		
		var props = this.getConcatenatedDisplayProps(this._props), mtx = props.matrix;
		
		var visibility = props.visible ? "visible" : "hidden";
		if (visibility != style.visibility) { style.visibility = visibility; }
		if (!props.visible) { return; }
		
		var oldProps = this._oldProps, oldMtx = oldProps&&oldProps.matrix;
		var n = 10000; // precision
		
		if (!oldMtx || !oldMtx.equals(mtx)) {
			var str = "matrix(" + (mtx.a*n|0)/n +","+ (mtx.b*n|0)/n +","+ (mtx.c*n|0)/n +","+ (mtx.d*n|0)/n +","+ (mtx.tx+0.5|0);
			style.transform = style.WebkitTransform = style.OTransform = style.msTransform = str +","+ (mtx.ty+0.5|0) +")";
			style.MozTransform = str +"px,"+ (mtx.ty+0.5|0) +"px)";
			if (!oldProps) { oldProps = this._oldProps = new createjs.DisplayProps(true, NaN); }
			oldProps.matrix.copy(mtx);
		}
		
		if (oldProps.alpha != props.alpha) {
			style.opacity = ""+(props.alpha*n|0)/n;
			oldProps.alpha = props.alpha;
		}
	};


	createjs.DOMElement = createjs.promote(DOMElement, "DisplayObject");
}());

//##############################################################################
// Filter.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Base class that all filters should inherit from. Filters need to be applied to objects that have been cached using
	 * the {{#crossLink "DisplayObject/cache"}}{{/crossLink}} method. If an object changes, please cache it again, or use
	 * {{#crossLink "DisplayObject/updateCache"}}{{/crossLink}}. Note that the filters must be applied before caching.
	 *
	 * <h4>Example</h4>
	 *
	 *      myInstance.filters = [
	 *          new createjs.ColorFilter(0, 0, 0, 1, 255, 0, 0),
	 *          new createjs.BlurFilter(5, 5, 10)
	 *      ];
	 *      myInstance.cache(0,0, 100, 100);
	 *
	 * Note that each filter can implement a {{#crossLink "Filter/getBounds"}}{{/crossLink}} method, which returns the
	 * margins that need to be applied in order to fully display the filter. For example, the {{#crossLink "BlurFilter"}}{{/crossLink}}
	 * will cause an object to feather outwards, resulting in a margin around the shape.
	 *
	 * <h4>EaselJS Filters</h4>
	 * EaselJS comes with a number of pre-built filters:
	 * <ul><li>{{#crossLink "AlphaMapFilter"}}{{/crossLink}} : Map a greyscale image to the alpha channel of a display object</li>
	 *      <li>{{#crossLink "AlphaMaskFilter"}}{{/crossLink}}: Map an image's alpha channel to the alpha channel of a display object</li>
	 *      <li>{{#crossLink "BlurFilter"}}{{/crossLink}}: Apply vertical and horizontal blur to a display object</li>
	 *      <li>{{#crossLink "ColorFilter"}}{{/crossLink}}: Color transform a display object</li>
	 *      <li>{{#crossLink "ColorMatrixFilter"}}{{/crossLink}}: Transform an image using a {{#crossLink "ColorMatrix"}}{{/crossLink}}</li>
	 * </ul>
	 *
	 * @class Filter
	 * @constructor
	 **/
	function Filter() {}
	var p = Filter.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// public methods:
	/**
	 * Provides padding values for this filter. That is, how much the filter will extend the visual bounds of an object it is applied to.
	 * @method getBounds
	 * @param {Rectangle} [rect] If specified, the provided Rectangle instance will be expanded by the padding amounts and returned.
	 * @return {Rectangle} If a `rect` param was provided, it is returned. If not, either a new rectangle with the padding values, or null if no padding is required for this filter.
	 **/
	p.getBounds = function(rect) {
		return rect;
	};

	/**
	 * Applies the filter to the specified context.
	 * @method applyFilter
	 * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
	 * @param {Number} x The x position to use for the source rect.
	 * @param {Number} y The y position to use for the source rect.
	 * @param {Number} width The width to use for the source rect.
	 * @param {Number} height The height to use for the source rect.
	 * @param {CanvasRenderingContext2D} [targetCtx] The 2D context to draw the result to. Defaults to the context passed to ctx.
	 * @param {Number} [targetX] The x position to draw the result to. Defaults to the value passed to x.
	 * @param {Number} [targetY] The y position to draw the result to. Defaults to the value passed to y.
	 * @return {Boolean} If the filter was applied successfully.
	 **/
	p.applyFilter = function(ctx, x, y, width, height, targetCtx, targetX, targetY) {
		// this is the default behaviour because most filters access pixel data. It is overridden when not needed.
		targetCtx = targetCtx || ctx;
		if (targetX == null) { targetX = x; }
		if (targetY == null) { targetY = y; }
		try {
			var imageData = ctx.getImageData(x, y, width, height);
		} catch (e) {
			return false;
		}
		if (this._applyFilter(imageData)) {
			targetCtx.putImageData(imageData, targetX, targetY);
			return true;
		}
		return false;
	};

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[Filter]";
	};

	/**
	 * Returns a clone of this Filter instance.
	 * @method clone
	 * @return {Filter} A clone of the current Filter instance.
	 **/
	p.clone = function() {
		return new Filter();
	};
	
// private methods:
	/**
	 * @method _applyFilter
	 * @param {ImageData} imageData Target ImageData instance.
	 * @return {Boolean}
	 **/
	p._applyFilter = function(imageData) { return true; };


	createjs.Filter = Filter;
}());

//##############################################################################
// BlurFilter.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Applies a box blur to DisplayObjects. Note that this filter is fairly CPU intensive, particularly if the quality is
	 * set higher than 1.
	 *
	 * <h4>Example</h4>
	 * This example creates a red circle, and then applies a 5 pixel blur to it. It uses the {{#crossLink "Filter/getBounds"}}{{/crossLink}}
	 * method to account for the spread that the blur causes.
	 *
	 *      var shape = new createjs.Shape().set({x:100,y:100});
	 *      shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
	 *
	 *      var blurFilter = new createjs.BlurFilter(5, 5, 1);
	 *      shape.filters = [blurFilter];
	 *      var bounds = blurFilter.getBounds();
	 *
	 *      shape.cache(-50+bounds.x, -50+bounds.y, 100+bounds.width, 100+bounds.height);
	 *
	 * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
	 * @class BlurFilter
	 * @extends Filter
	 * @constructor
	 * @param {Number} [blurX=0] The horizontal blur radius in pixels.
	 * @param {Number} [blurY=0] The vertical blur radius in pixels.
	 * @param {Number} [quality=1] The number of blur iterations.
	 **/
	function BlurFilter( blurX, blurY, quality) {
		if ( isNaN(blurX) || blurX < 0 ) blurX = 0;
		if ( isNaN(blurY) || blurY < 0 ) blurY = 0;
		if ( isNaN(quality) || quality < 1  ) quality = 1;


		// public properties:
		/**
		 * Horizontal blur radius in pixels
		 * @property blurX
		 * @default 0
		 * @type Number
		 **/
		this.blurX = blurX | 0;

		/**
		 * Vertical blur radius in pixels
		 * @property blurY
		 * @default 0
		 * @type Number
		 **/
		this.blurY = blurY | 0;

		/**
		 * Number of blur iterations. For example, a value of 1 will produce a rough blur. A value of 2 will produce a
		 * smoother blur, but take twice as long to run.
		 * @property quality
		 * @default 1
		 * @type Number
		 **/
		this.quality = quality | 0;
	}
	var p = createjs.extend(BlurFilter, createjs.Filter);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// constants:
	/**
	 * Array of multiply values for blur calculations.
	 * @property MUL_TABLE
	 * @type Array
	 * @protected
	 * @static
	 **/
	BlurFilter.MUL_TABLE = [1, 171, 205, 293, 57, 373, 79, 137, 241, 27, 391, 357, 41, 19, 283, 265, 497, 469, 443, 421, 25, 191, 365, 349, 335, 161, 155, 149, 9, 278, 269, 261, 505, 245, 475, 231, 449, 437, 213, 415, 405, 395, 193, 377, 369, 361, 353, 345, 169, 331, 325, 319, 313, 307, 301, 37, 145, 285, 281, 69, 271, 267, 263, 259, 509, 501, 493, 243, 479, 118, 465, 459, 113, 446, 55, 435, 429, 423, 209, 413, 51, 403, 199, 393, 97, 3, 379, 375, 371, 367, 363, 359, 355, 351, 347, 43, 85, 337, 333, 165, 327, 323, 5, 317, 157, 311, 77, 305, 303, 75, 297, 294, 73, 289, 287, 71, 141, 279, 277, 275, 68, 135, 67, 133, 33, 262, 260, 129, 511, 507, 503, 499, 495, 491, 61, 121, 481, 477, 237, 235, 467, 232, 115, 457, 227, 451, 7, 445, 221, 439, 218, 433, 215, 427, 425, 211, 419, 417, 207, 411, 409, 203, 202, 401, 399, 396, 197, 49, 389, 387, 385, 383, 95, 189, 47, 187, 93, 185, 23, 183, 91, 181, 45, 179, 89, 177, 11, 175, 87, 173, 345, 343, 341, 339, 337, 21, 167, 83, 331, 329, 327, 163, 81, 323, 321, 319, 159, 79, 315, 313, 39, 155, 309, 307, 153, 305, 303, 151, 75, 299, 149, 37, 295, 147, 73, 291, 145, 289, 287, 143, 285, 71, 141, 281, 35, 279, 139, 69, 275, 137, 273, 17, 271, 135, 269, 267, 133, 265, 33, 263, 131, 261, 130, 259, 129, 257, 1];

	/**
	 * Array of shift values for blur calculations.
	 * @property SHG_TABLE
	 * @type Array
	 * @protected
	 * @static
	 **/
	BlurFilter.SHG_TABLE = [0, 9, 10, 11, 9, 12, 10, 11, 12, 9, 13, 13, 10, 9, 13, 13, 14, 14, 14, 14, 10, 13, 14, 14, 14, 13, 13, 13, 9, 14, 14, 14, 15, 14, 15, 14, 15, 15, 14, 15, 15, 15, 14, 15, 15, 15, 15, 15, 14, 15, 15, 15, 15, 15, 15, 12, 14, 15, 15, 13, 15, 15, 15, 15, 16, 16, 16, 15, 16, 14, 16, 16, 14, 16, 13, 16, 16, 16, 15, 16, 13, 16, 15, 16, 14, 9, 16, 16, 16, 16, 16, 16, 16, 16, 16, 13, 14, 16, 16, 15, 16, 16, 10, 16, 15, 16, 14, 16, 16, 14, 16, 16, 14, 16, 16, 14, 15, 16, 16, 16, 14, 15, 14, 15, 13, 16, 16, 15, 17, 17, 17, 17, 17, 17, 14, 15, 17, 17, 16, 16, 17, 16, 15, 17, 16, 17, 11, 17, 16, 17, 16, 17, 16, 17, 17, 16, 17, 17, 16, 17, 17, 16, 16, 17, 17, 17, 16, 14, 17, 17, 17, 17, 15, 16, 14, 16, 15, 16, 13, 16, 15, 16, 14, 16, 15, 16, 12, 16, 15, 16, 17, 17, 17, 17, 17, 13, 16, 15, 17, 17, 17, 16, 15, 17, 17, 17, 16, 15, 17, 17, 14, 16, 17, 17, 16, 17, 17, 16, 15, 17, 16, 14, 17, 16, 15, 17, 16, 17, 17, 16, 17, 15, 16, 17, 14, 17, 16, 15, 17, 16, 17, 13, 17, 16, 17, 17, 16, 17, 14, 17, 16, 17, 16, 17, 16, 17, 9];

// public methods:
	/** docced in super class **/
	p.getBounds = function (rect) {
		var x = this.blurX|0, y = this.blurY| 0;
		if (x <= 0 && y <= 0) { return rect; }
		var q = Math.pow(this.quality, 0.2);
		return (rect || new createjs.Rectangle()).pad(x*q+1,y*q+1,x*q+1,y*q+1);
	};

	/** docced in super class **/
	p.clone = function() {
		return new BlurFilter(this.blurX, this.blurY, this.quality);
	};

	/** docced in super class **/
	p.toString = function() {
		return "[BlurFilter]";
	};


// private methods:

	/** docced in super class **/
	p._applyFilter = function (imageData) {

		var radiusX = this.blurX >> 1;
		if (isNaN(radiusX) || radiusX < 0) return false;
		var radiusY = this.blurY >> 1;
		if (isNaN(radiusY) || radiusY < 0) return false;
		if (radiusX == 0 && radiusY == 0) return false;

		var iterations = this.quality;
		if (isNaN(iterations) || iterations < 1) iterations = 1;
		iterations |= 0;
		if (iterations > 3) iterations = 3;
		if (iterations < 1) iterations = 1;

		var px = imageData.data;
		var x=0, y=0, i=0, p=0, yp=0, yi=0, yw=0, r=0, g=0, b=0, a=0, pr=0, pg=0, pb=0, pa=0;

		var divx = (radiusX + radiusX + 1) | 0;
		var divy = (radiusY + radiusY + 1) | 0;
		var w = imageData.width | 0;
		var h = imageData.height | 0;

		var w1 = (w - 1) | 0;
		var h1 = (h - 1) | 0;
		var rxp1 = (radiusX + 1) | 0;
		var ryp1 = (radiusY + 1) | 0;

		var ssx = {r:0,b:0,g:0,a:0};
		var sx = ssx;
		for ( i = 1; i < divx; i++ )
		{
			sx = sx.n = {r:0,b:0,g:0,a:0};
		}
		sx.n = ssx;

		var ssy = {r:0,b:0,g:0,a:0};
		var sy = ssy;
		for ( i = 1; i < divy; i++ )
		{
			sy = sy.n = {r:0,b:0,g:0,a:0};
		}
		sy.n = ssy;

		var si = null;


		var mtx = BlurFilter.MUL_TABLE[radiusX] | 0;
		var stx = BlurFilter.SHG_TABLE[radiusX] | 0;
		var mty = BlurFilter.MUL_TABLE[radiusY] | 0;
		var sty = BlurFilter.SHG_TABLE[radiusY] | 0;

		while (iterations-- > 0) {

			yw = yi = 0;
			var ms = mtx;
			var ss = stx;
			for (y = h; --y > -1;) {
				r = rxp1 * (pr = px[(yi) | 0]);
				g = rxp1 * (pg = px[(yi + 1) | 0]);
				b = rxp1 * (pb = px[(yi + 2) | 0]);
				a = rxp1 * (pa = px[(yi + 3) | 0]);

				sx = ssx;

				for( i = rxp1; --i > -1; )
				{
					sx.r = pr;
					sx.g = pg;
					sx.b = pb;
					sx.a = pa;
					sx = sx.n;
				}

				for( i = 1; i < rxp1; i++ )
				{
					p = (yi + ((w1 < i ? w1 : i) << 2)) | 0;
					r += ( sx.r = px[p]);
					g += ( sx.g = px[p+1]);
					b += ( sx.b = px[p+2]);
					a += ( sx.a = px[p+3]);

					sx = sx.n;
				}

				si = ssx;
				for ( x = 0; x < w; x++ )
				{
					px[yi++] = (r * ms) >>> ss;
					px[yi++] = (g * ms) >>> ss;
					px[yi++] = (b * ms) >>> ss;
					px[yi++] = (a * ms) >>> ss;

					p = ((yw + ((p = x + radiusX + 1) < w1 ? p : w1)) << 2);

					r -= si.r - ( si.r = px[p]);
					g -= si.g - ( si.g = px[p+1]);
					b -= si.b - ( si.b = px[p+2]);
					a -= si.a - ( si.a = px[p+3]);

					si = si.n;

				}
				yw += w;
			}

			ms = mty;
			ss = sty;
			for (x = 0; x < w; x++) {
				yi = (x << 2) | 0;

				r = (ryp1 * (pr = px[yi])) | 0;
				g = (ryp1 * (pg = px[(yi + 1) | 0])) | 0;
				b = (ryp1 * (pb = px[(yi + 2) | 0])) | 0;
				a = (ryp1 * (pa = px[(yi + 3) | 0])) | 0;

				sy = ssy;
				for( i = 0; i < ryp1; i++ )
				{
					sy.r = pr;
					sy.g = pg;
					sy.b = pb;
					sy.a = pa;
					sy = sy.n;
				}

				yp = w;

				for( i = 1; i <= radiusY; i++ )
				{
					yi = ( yp + x ) << 2;

					r += ( sy.r = px[yi]);
					g += ( sy.g = px[yi+1]);
					b += ( sy.b = px[yi+2]);
					a += ( sy.a = px[yi+3]);

					sy = sy.n;

					if( i < h1 )
					{
						yp += w;
					}
				}

				yi = x;
				si = ssy;
				if ( iterations > 0 )
				{
					for ( y = 0; y < h; y++ )
					{
						p = yi << 2;
						px[p+3] = pa =(a * ms) >>> ss;
						if ( pa > 0 )
						{
							px[p]   = ((r * ms) >>> ss );
							px[p+1] = ((g * ms) >>> ss );
							px[p+2] = ((b * ms) >>> ss );
						} else {
							px[p] = px[p+1] = px[p+2] = 0
						}

						p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2;

						r -= si.r - ( si.r = px[p]);
						g -= si.g - ( si.g = px[p+1]);
						b -= si.b - ( si.b = px[p+2]);
						a -= si.a - ( si.a = px[p+3]);

						si = si.n;

						yi += w;
					}
				} else {
					for ( y = 0; y < h; y++ )
					{
						p = yi << 2;
						px[p+3] = pa =(a * ms) >>> ss;
						if ( pa > 0 )
						{
							pa = 255 / pa;
							px[p]   = ((r * ms) >>> ss ) * pa;
							px[p+1] = ((g * ms) >>> ss ) * pa;
							px[p+2] = ((b * ms) >>> ss ) * pa;
						} else {
							px[p] = px[p+1] = px[p+2] = 0
						}

						p = ( x + (( ( p = y + ryp1) < h1 ? p : h1 ) * w )) << 2;

						r -= si.r - ( si.r = px[p]);
						g -= si.g - ( si.g = px[p+1]);
						b -= si.b - ( si.b = px[p+2]);
						a -= si.a - ( si.a = px[p+3]);

						si = si.n;

						yi += w;
					}
				}
			}

		}
		return true;
	};

	createjs.BlurFilter = createjs.promote(BlurFilter, "Filter");
}());

//##############################################################################
// AlphaMapFilter.js
//##############################################################################

(function () {
	"use strict";
	
	
// constructor:
	/**
	 * Applies a greyscale alpha map image (or canvas) to the target, such that the alpha channel of the result will
	 * be copied from the red channel of the map, and the RGB channels will be copied from the target.
	 *
	 * Generally, it is recommended that you use {{#crossLink "AlphaMaskFilter"}}{{/crossLink}}, because it has much
	 * better performance.
	 *
	 * <h4>Example</h4>
	 * This example draws a red->blue box, caches it, and then uses the cache canvas as an alpha map on a 100x100 image.
	 *
	 *       var box = new createjs.Shape();
	 *       box.graphics.beginLinearGradientFill(["#ff0000", "#0000ff"], [0, 1], 0, 0, 0, 100)
	 *       box.graphics.drawRect(0, 0, 100, 100);
	 *       box.cache(0, 0, 100, 100);
	 *
	 *       var bmp = new createjs.Bitmap("path/to/image.jpg");
	 *       bmp.filters = [
	 *           new createjs.AlphaMapFilter(box.cacheCanvas)
	 *       ];
	 *       bmp.cache(0, 0, 100, 100);
	 *       stage.addChild(bmp);
	 *
	 * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
	 * @class AlphaMapFilter
	 * @extends Filter
	 * @constructor
	 * @param {HTMLImageElement|HTMLCanvasElement} alphaMap The greyscale image (or canvas) to use as the alpha value for the
	 * result. This should be exactly the same dimensions as the target.
	 **/
	function AlphaMapFilter(alphaMap) {
	
	
	// public properties:
		/**
		 * The greyscale image (or canvas) to use as the alpha value for the result. This should be exactly the same
		 * dimensions as the target.
		 * @property alphaMap
		 * @type HTMLImageElement|HTMLCanvasElement
		 **/
		this.alphaMap = alphaMap;
		
		
	// private properties:
		/**
		 * @property _alphaMap
		 * @protected
		 * @type HTMLImageElement|HTMLCanvasElement
		 **/
		this._alphaMap = null;
		
		/**
		 * @property _mapData
		 * @protected
		 * @type Uint8ClampedArray
		 **/
		this._mapData = null;
	}
	var p = createjs.extend(AlphaMapFilter, createjs.Filter);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// public methods:
	/** docced in super class **/
	p.clone = function () {
		var o = new AlphaMapFilter(this.alphaMap);
		o._alphaMap = this._alphaMap;
		o._mapData = this._mapData;
		return o;
	};

	/** docced in super class **/
	p.toString = function () {
		return "[AlphaMapFilter]";
	};


// private methods:
	/** docced in super class **/
	p._applyFilter = function (imageData) {
		if (!this.alphaMap) { return true; }
		if (!this._prepAlphaMap()) { return false; }
		
		// TODO: update to support scenarios where the target has different dimensions.
		var data = imageData.data;
		var map = this._mapData;
		for(var i=0, l=data.length; i<l; i += 4) { data[i + 3] = map[i] || 0; }
		
		return true;
	};

	/**
	 * @method _prepAlphaMap
	 * @protected
	 **/
	p._prepAlphaMap = function () {
		if (!this.alphaMap) { return false; }
		if (this.alphaMap == this._alphaMap && this._mapData) { return true; }

		this._mapData = null;
		var map = this._alphaMap = this.alphaMap;
		var canvas = map;
		var ctx;
		if (map instanceof HTMLCanvasElement) {
			ctx = canvas.getContext("2d");
		} else {
			canvas = createjs.createCanvas ? createjs.createCanvas() : document.createElement("canvas");
			canvas.width = map.width;
			canvas.height = map.height;
			ctx = canvas.getContext("2d");
			ctx.drawImage(map, 0, 0);
		}

		try {
			var imgData = ctx.getImageData(0, 0, map.width, map.height);
		} catch (e) {
			//if (!this.suppressCrossDomainErrors) throw new Error("unable to access local image data: " + e);
			return false;
		}
		
		this._mapData = imgData.data;
		return true;
	};


	createjs.AlphaMapFilter = createjs.promote(AlphaMapFilter, "Filter");
}());

//##############################################################################
// AlphaMaskFilter.js
//##############################################################################

(function () {
	"use strict";


// constructor:
	/**
	 * Applies the alpha from the mask image (or canvas) to the target, such that the alpha channel of the result will
	 * be derived from the mask, and the RGB channels will be copied from the target. This can be used, for example, to
	 * apply an alpha mask to a display object. This can also be used to combine a JPG compressed RGB image with a PNG32
	 * alpha mask, which can result in a much smaller file size than a single PNG32 containing ARGB.
	 *
	 * <b>IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters correctly.</b>
	 *
	 * <h4>Example</h4>
	 * This example draws a gradient box, then caches it and uses the "cacheCanvas" as the alpha mask on a 100x100 image.
	 *
	 *      var box = new createjs.Shape();
	 *      box.graphics.beginLinearGradientFill(["#000000", "rgba(0, 0, 0, 0)"], [0, 1], 0, 0, 100, 100)
	 *      box.graphics.drawRect(0, 0, 100, 100);
	 *      box.cache(0, 0, 100, 100);
	 *
	 *      var bmp = new createjs.Bitmap("path/to/image.jpg");
	 *      bmp.filters = [
	 *          new createjs.AlphaMaskFilter(box.cacheCanvas)
	 *      ];
	 *      bmp.cache(0, 0, 100, 100);
	 *
	 * See {{#crossLink "Filter"}}{{/crossLink}} for more information on applying filters.
	 * @class AlphaMaskFilter
	 * @extends Filter
	 * @constructor
	 * @param {HTMLImageElement|HTMLCanvasElement} mask
	 **/
	function AlphaMaskFilter(mask) {
	
	
	// public properties:
		/**
		 * The image (or canvas) to use as the mask.
		 * @property mask
		 * @type HTMLImageElement|HTMLCanvasElement
		 **/
		this.mask = mask;
	}
	var p = createjs.extend(AlphaMaskFilter, createjs.Filter);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
	

// public methods:
	/**
	 * Applies the filter to the specified context.
	 *
	 * <strong>IMPORTANT NOTE: This filter currently does not support the targetCtx, or targetX/Y parameters
	 * correctly.</strong>
	 * @method applyFilter
	 * @param {CanvasRenderingContext2D} ctx The 2D context to use as the source.
	 * @param {Number} x The x position to use for the source rect.
	 * @param {Number} y The y position to use for the source rect.
	 * @param {Number} width The width to use for the source rect.
	 * @param {Number} height The height to use for the source rect.
	 * @param {CanvasRenderingContext2D} [targetCtx] NOT SUPPORTED IN THIS FILTER. The 2D context to draw the result to. Defaults to the context passed to ctx.
	 * @param {Number} [targetX] NOT SUPPORTED IN THIS FILTER. The x position to draw the result to. Defaults to the value passed to x.
	 * @param {Number} [targetY] NOT SUPPORTED IN THIS FILTER. The y position to draw the result to. Defaults to the value passed to y.
	 * @return {Boolean} If the filter was applied successfully.
	 **/
	p.applyFilter = function (ctx, x, y, width, height, targetCtx, targetX, targetY) {
		if (!this.mask) { return true; }
		targetCtx = targetCtx || ctx;
		if (targetX == null) { targetX = x; }
		if (targetY == null) { targetY = y; }

		targetCtx.save();
		if (ctx != targetCtx) {
			// TODO: support targetCtx and targetX/Y
			// clearRect, then draw the ctx in?
			return false;
		}

		targetCtx.globalCompositeOperation = "destination-in";
		targetCtx.drawImage(this.mask, targetX, targetY);
		targetCtx.restore();
		return true;
	};

	/** docced in super class **/
	p.clone = function () {
		return new AlphaMaskFilter(this.mask);
	};

	/** docced in super class **/
	p.toString = function () {
		return "[AlphaMaskFilter]";
	};


	createjs.AlphaMaskFilter = createjs.promote(AlphaMaskFilter, "Filter");
}());

//##############################################################################
// ColorFilter.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Applies a color transform to DisplayObjects.
	 *
	 * <h4>Example</h4>
	 * This example draws a red circle, and then transforms it to Blue. This is accomplished by multiplying all the channels
	 * to 0 (except alpha, which is set to 1), and then adding 255 to the blue channel.
	 *
	 *      var shape = new createjs.Shape().set({x:100,y:100});
	 *      shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
	 *
	 *      shape.filters = [
	 *          new createjs.ColorFilter(0,0,0,1, 0,0,255,0)
	 *      ];
	 *      shape.cache(-50, -50, 100, 100);
	 *
	 * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
	 * @class ColorFilter
	 * @param {Number} [redMultiplier=1] The amount to multiply against the red channel. This is a range between 0 and 1.
	 * @param {Number} [greenMultiplier=1] The amount to multiply against the green channel. This is a range between 0 and 1.
	 * @param {Number} [blueMultiplier=1] The amount to multiply against the blue channel. This is a range between 0 and 1.
	 * @param {Number} [alphaMultiplier=1] The amount to multiply against the alpha channel. This is a range between 0 and 1.
	 * @param {Number} [redOffset=0] The amount to add to the red channel after it has been multiplied. This is a range
	 * between -255 and 255.
	 * @param {Number} [greenOffset=0] The amount to add to the green channel after it has been multiplied. This is a range
	  * between -255 and 255.
	 * @param {Number} [blueOffset=0] The amount to add to the blue channel after it has been multiplied. This is a range
	  * between -255 and 255.
	 * @param {Number} [alphaOffset=0] The amount to add to the alpha channel after it has been multiplied. This is a range
	  * between -255 and 255.
	 * @constructor
	 * @extends Filter
	 **/
	function ColorFilter(redMultiplier, greenMultiplier, blueMultiplier, alphaMultiplier, redOffset, greenOffset, blueOffset, alphaOffset) {
		
	
	// public properties:
		/**
		 * Red channel multiplier.
		 * @property redMultiplier
		 * @type Number
		 **/
		this.redMultiplier = redMultiplier != null ? redMultiplier : 1;
	
		/**
		 * Green channel multiplier.
		 * @property greenMultiplier
		 * @type Number
		 **/
		this.greenMultiplier = greenMultiplier != null ? greenMultiplier : 1;
	
		/**
		 * Blue channel multiplier.
		 * @property blueMultiplier
		 * @type Number
		 **/
		this.blueMultiplier = blueMultiplier != null ? blueMultiplier : 1;
	
		/**
		 * Alpha channel multiplier.
		 * @property alphaMultiplier
		 * @type Number
		 **/
		this.alphaMultiplier = alphaMultiplier != null ? alphaMultiplier : 1;
	
		/**
		 * Red channel offset (added to value).
		 * @property redOffset
		 * @type Number
		 **/
		this.redOffset = redOffset || 0;
	
		/**
		 * Green channel offset (added to value).
		 * @property greenOffset
		 * @type Number
		 **/
		this.greenOffset = greenOffset || 0;
	
		/**
		 * Blue channel offset (added to value).
		 * @property blueOffset
		 * @type Number
		 **/
		this.blueOffset = blueOffset || 0;
	
		/**
		 * Alpha channel offset (added to value).
		 * @property alphaOffset
		 * @type Number
		 **/
		this.alphaOffset = alphaOffset || 0;
	}
	var p = createjs.extend(ColorFilter, createjs.Filter);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// public methods:
	/** docced in super class **/
	p.toString = function() {
		return "[ColorFilter]";
	};

	/** docced in super class **/
	p.clone = function() {
		return new ColorFilter(this.redMultiplier, this.greenMultiplier, this.blueMultiplier, this.alphaMultiplier, this.redOffset, this.greenOffset, this.blueOffset, this.alphaOffset);
	};
	

// private methods:
	/** docced in super class **/
	p._applyFilter = function(imageData) {
		var data = imageData.data;
		var l = data.length;
		for (var i=0; i<l; i+=4) {
			data[i] = data[i]*this.redMultiplier+this.redOffset;
			data[i+1] = data[i+1]*this.greenMultiplier+this.greenOffset;
			data[i+2] = data[i+2]*this.blueMultiplier+this.blueOffset;
			data[i+3] = data[i+3]*this.alphaMultiplier+this.alphaOffset;
		}
		return true;
	};


	createjs.ColorFilter = createjs.promote(ColorFilter, "Filter");
}());

//##############################################################################
// ColorMatrix.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Provides helper functions for assembling a matrix for use with the {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}.
	 * Most methods return the instance to facilitate chained calls.
	 *
	 * <h4>Example</h4>
	 *
	 *      myColorMatrix.adjustHue(20).adjustBrightness(50);
	 *
	 * See {{#crossLink "Filter"}}{{/crossLink}} for an example of how to apply filters, or {{#crossLink "ColorMatrixFilter"}}{{/crossLink}}
	 * for an example of how to use ColorMatrix to change a DisplayObject's color.
	 * @class ColorMatrix
	 * @param {Number} brightness
	 * @param {Number} contrast
	 * @param {Number} saturation
	 * @param {Number} hue
	 * @constructor
	 **/
	function ColorMatrix(brightness, contrast, saturation, hue) {
		this.setColor(brightness, contrast, saturation, hue);
	}
	var p = ColorMatrix.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// constants:
	/**
	 * Array of delta values for contrast calculations.
	 * @property DELTA_INDEX
	 * @type Array
	 * @protected
	 * @static
	 **/
	ColorMatrix.DELTA_INDEX = [
		0,    0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1,  0.11,
		0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
		0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
		0.44, 0.46, 0.48, 0.5,  0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
		0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
		1.0,  1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
		1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0,  2.12, 2.25,
		2.37, 2.50, 2.62, 2.75, 2.87, 3.0,  3.2,  3.4,  3.6,  3.8,
		4.0,  4.3,  4.7,  4.9,  5.0,  5.5,  6.0,  6.5,  6.8,  7.0,
		7.3,  7.5,  7.8,  8.0,  8.4,  8.7,  9.0,  9.4,  9.6,  9.8,
		10.0
	];

	/**
	 * Identity matrix values.
	 * @property IDENTITY_MATRIX
	 * @type Array
	 * @protected
	 * @static
	 **/
	ColorMatrix.IDENTITY_MATRIX = [
		1,0,0,0,0,
		0,1,0,0,0,
		0,0,1,0,0,
		0,0,0,1,0,
		0,0,0,0,1
	];

	/**
	 * The constant length of a color matrix.
	 * @property LENGTH
	 * @type Number
	 * @protected
	 * @static
	 **/
	ColorMatrix.LENGTH = ColorMatrix.IDENTITY_MATRIX.length;


// public methods:
	/**
	 * Resets the instance with the specified values.
	 * @method setColor
	 * @param {Number} brightness
	 * @param {Number} contrast
	 * @param {Number} saturation
	 * @param {Number} hue
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 */
	p.setColor = function(brightness,contrast,saturation,hue) {
		return this.reset().adjustColor(brightness,contrast,saturation,hue);
	};

	/**
	 * Resets the matrix to identity values.
	 * @method reset
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 */
	p.reset = function() {
		return this.copy(ColorMatrix.IDENTITY_MATRIX);
	};

	/**
	 * Shortcut method to adjust brightness, contrast, saturation and hue.
	 * Equivalent to calling adjustHue(hue), adjustContrast(contrast),
	 * adjustBrightness(brightness), adjustSaturation(saturation), in that order.
	 * @method adjustColor
	 * @param {Number} brightness
	 * @param {Number} contrast
	 * @param {Number} saturation
	 * @param {Number} hue
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.adjustColor = function(brightness,contrast,saturation,hue) {
		this.adjustHue(hue);
		this.adjustContrast(contrast);
		this.adjustBrightness(brightness);
		return this.adjustSaturation(saturation);
	};

	/**
	 * Adjusts the brightness of pixel color by adding the specified value to the red, green and blue channels.
	 * Positive values will make the image brighter, negative values will make it darker.
	 * @method adjustBrightness
	 * @param {Number} value A value between -255 & 255 that will be added to the RGB channels.
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.adjustBrightness = function(value) {
		if (value == 0 || isNaN(value)) { return this; }
		value = this._cleanValue(value,255);
		this._multiplyMatrix([
			1,0,0,0,value,
			0,1,0,0,value,
			0,0,1,0,value,
			0,0,0,1,0,
			0,0,0,0,1
		]);
		return this;
	};

	/**
	 * Adjusts the contrast of pixel color.
	 * Positive values will increase contrast, negative values will decrease contrast.
	 * @method adjustContrast
	 * @param {Number} value A value between -100 & 100.
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.adjustContrast = function(value) {
		if (value == 0 || isNaN(value)) { return this; }
		value = this._cleanValue(value,100);
		var x;
		if (value<0) {
			x = 127+value/100*127;
		} else {
			x = value%1;
			if (x == 0) {
				x = ColorMatrix.DELTA_INDEX[value];
			} else {
				x = ColorMatrix.DELTA_INDEX[(value<<0)]*(1-x)+ColorMatrix.DELTA_INDEX[(value<<0)+1]*x; // use linear interpolation for more granularity.
			}
			x = x*127+127;
		}
		this._multiplyMatrix([
			x/127,0,0,0,0.5*(127-x),
			0,x/127,0,0,0.5*(127-x),
			0,0,x/127,0,0.5*(127-x),
			0,0,0,1,0,
			0,0,0,0,1
		]);
		return this;
	};

	/**
	 * Adjusts the color saturation of the pixel.
	 * Positive values will increase saturation, negative values will decrease saturation (trend towards greyscale).
	 * @method adjustSaturation
	 * @param {Number} value A value between -100 & 100.
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.adjustSaturation = function(value) {
		if (value == 0 || isNaN(value)) { return this; }
		value = this._cleanValue(value,100);
		var x = 1+((value > 0) ? 3*value/100 : value/100);
		var lumR = 0.3086;
		var lumG = 0.6094;
		var lumB = 0.0820;
		this._multiplyMatrix([
			lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0,
			lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0,
			lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0,
			0,0,0,1,0,
			0,0,0,0,1
		]);
		return this;
	};


	/**
	 * Adjusts the hue of the pixel color.
	 * @method adjustHue
	 * @param {Number} value A value between -180 & 180.
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.adjustHue = function(value) {
		if (value == 0 || isNaN(value)) { return this; }
		value = this._cleanValue(value,180)/180*Math.PI;
		var cosVal = Math.cos(value);
		var sinVal = Math.sin(value);
		var lumR = 0.213;
		var lumG = 0.715;
		var lumB = 0.072;
		this._multiplyMatrix([
			lumR+cosVal*(1-lumR)+sinVal*(-lumR),lumG+cosVal*(-lumG)+sinVal*(-lumG),lumB+cosVal*(-lumB)+sinVal*(1-lumB),0,0,
			lumR+cosVal*(-lumR)+sinVal*(0.143),lumG+cosVal*(1-lumG)+sinVal*(0.140),lumB+cosVal*(-lumB)+sinVal*(-0.283),0,0,
			lumR+cosVal*(-lumR)+sinVal*(-(1-lumR)),lumG+cosVal*(-lumG)+sinVal*(lumG),lumB+cosVal*(1-lumB)+sinVal*(lumB),0,0,
			0,0,0,1,0,
			0,0,0,0,1
		]);
		return this;
	};

	/**
	 * Concatenates (multiplies) the specified matrix with this one.
	 * @method concat
	 * @param {Array} matrix An array or ColorMatrix instance.
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.concat = function(matrix) {
		matrix = this._fixMatrix(matrix);
		if (matrix.length != ColorMatrix.LENGTH) { return this; }
		this._multiplyMatrix(matrix);
		return this;
	};

	/**
	 * Returns a clone of this ColorMatrix.
	 * @method clone
	 * @return {ColorMatrix} A clone of this ColorMatrix.
	 **/
	p.clone = function() {
		return (new ColorMatrix()).copy(this);
	};

	/**
	 * Return a length 25 (5x5) array instance containing this matrix's values.
	 * @method toArray
	 * @return {Array} An array holding this matrix's values.
	 **/
	p.toArray = function() {
		var arr = [];
		for (var i= 0, l=ColorMatrix.LENGTH; i<l; i++) {
			arr[i] = this[i];
		}
		return arr;
	};

	/**
	 * Copy the specified matrix's values to this matrix.
	 * @method copy
	 * @param {Array} matrix An array or ColorMatrix instance.
	 * @return {ColorMatrix} The ColorMatrix instance the method is called on (useful for chaining calls.)
	 * @chainable
	 **/
	p.copy = function(matrix) {
		var l = ColorMatrix.LENGTH;
		for (var i=0;i<l;i++) {
			this[i] = matrix[i];
		}
		return this;
	};
	
	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 **/
	p.toString = function() {
		return "[ColorMatrix]";
	};


// private methods:
	/**
	 * @method _multiplyMatrix
	 * @param {Array} matrix
	 * @protected
	 **/
	p._multiplyMatrix = function(matrix) {
		var i, j, k, col = [];

		for (i=0;i<5;i++) {
			for (j=0;j<5;j++) {
				col[j] = this[j+i*5];
			}
			for (j=0;j<5;j++) {
				var val=0;
				for (k=0;k<5;k++) {
					val += matrix[j+k*5]*col[k];
				}
				this[j+i*5] = val;
			}
		}
	};

	/**
	 * Make sure values are within the specified range, hue has a limit of 180, brightness is 255, others are 100.
	 * @method _cleanValue
	 * @param {Number} value The raw number
	 * @param {Number} limit The maximum that the number can be. The minimum is the limit * -1.
	 * @protected
	 **/
	p._cleanValue = function(value, limit) {
		return Math.min(limit,Math.max(-limit,value));
	};

	/**
	 * Makes sure matrixes are 5x5 (25 long).
	 * @method _fixMatrix
	 * @param {Array} matrix
	 * @protected
	 **/
	p._fixMatrix = function(matrix) {
		if (matrix instanceof ColorMatrix) { matrix = matrix.toArray(); }
		if (matrix.length < ColorMatrix.LENGTH) {
			matrix = matrix.slice(0,matrix.length).concat(ColorMatrix.IDENTITY_MATRIX.slice(matrix.length,ColorMatrix.LENGTH));
		} else if (matrix.length > ColorMatrix.LENGTH) {
			matrix = matrix.slice(0,ColorMatrix.LENGTH);
		}
		return matrix;
	};


	createjs.ColorMatrix = ColorMatrix;
}());

//##############################################################################
// ColorMatrixFilter.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
	 * Allows you to carry out complex color operations such as modifying saturation, brightness, or inverting. See the
	 * {{#crossLink "ColorMatrix"}}{{/crossLink}} for more information on changing colors. For an easier color transform,
	 * consider the {{#crossLink "ColorFilter"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 * This example creates a red circle, inverts its hue, and then saturates it to brighten it up.
	 *
	 *      var shape = new createjs.Shape().set({x:100,y:100});
	 *      shape.graphics.beginFill("#ff0000").drawCircle(0,0,50);
	 *
	 *      var matrix = new createjs.ColorMatrix().adjustHue(180).adjustSaturation(100);
	 *      shape.filters = [
	 *          new createjs.ColorMatrixFilter(matrix)
	 *      ];
	 *
	 *      shape.cache(-50, -50, 100, 100);
	 *
	 * See {{#crossLink "Filter"}}{{/crossLink}} for an more information on applying filters.
	 * @class ColorMatrixFilter
	 * @constructor
	 * @extends Filter
	 * @param {Array | ColorMatrix} matrix A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
	 * class.
	 **/
	function ColorMatrixFilter(matrix) {
	
		
	// public properties:
		/**
		 * A 4x5 matrix describing the color operation to perform. See also the {{#crossLink "ColorMatrix"}}{{/crossLink}}
		 * @property matrix
		 * @type Array | ColorMatrix
		 **/
		this.matrix = matrix;
	}
	var p = createjs.extend(ColorMatrixFilter, createjs.Filter);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
	

// public methods:
	/** docced in super class **/
	p.toString = function() {
		return "[ColorMatrixFilter]";
	};

	/** docced in super class **/
	p.clone = function() {
		return new ColorMatrixFilter(this.matrix);
	};

// private methods:
	/** docced in super class **/
	p._applyFilter = function(imageData) { 
		var data = imageData.data;
		var l = data.length;
		var r,g,b,a;
		var mtx = this.matrix;
		var m0 =  mtx[0],  m1 =  mtx[1],  m2 =  mtx[2],  m3 =  mtx[3],  m4 =  mtx[4];
		var m5 =  mtx[5],  m6 =  mtx[6],  m7 =  mtx[7],  m8 =  mtx[8],  m9 =  mtx[9];
		var m10 = mtx[10], m11 = mtx[11], m12 = mtx[12], m13 = mtx[13], m14 = mtx[14];
		var m15 = mtx[15], m16 = mtx[16], m17 = mtx[17], m18 = mtx[18], m19 = mtx[19];

		for (var i=0; i<l; i+=4) {
			r = data[i];
			g = data[i+1];
			b = data[i+2];
			a = data[i+3];
			data[i] = r*m0+g*m1+b*m2+a*m3+m4; // red
			data[i+1] = r*m5+g*m6+b*m7+a*m8+m9; // green
			data[i+2] = r*m10+g*m11+b*m12+a*m13+m14; // blue
			data[i+3] = r*m15+g*m16+b*m17+a*m18+m19; // alpha
		}
		return true;
	};


	createjs.ColorMatrixFilter = createjs.promote(ColorMatrixFilter, "Filter");
}());

//##############################################################################
// Touch.js
//##############################################################################

(function() {
	"use strict";


// constructor:
	/**
 * Global utility for working with multi-touch enabled devices in EaselJS. Currently supports W3C Touch API (iOS and
 * modern Android browser) and the Pointer API (IE), including ms-prefixed events in IE10, and unprefixed in IE11.
 *
 * Ensure that you {{#crossLink "Touch/disable"}}{{/crossLink}} touch when cleaning up your application. You do not have
 * to check if touch is supported to enable it, as it will fail gracefully if it is not supported.
 *
 * <h4>Example</h4>
 *
 *      var stage = new createjs.Stage("canvasId");
 *      createjs.Touch.enable(stage);
 *
 * <strong>Note:</strong> It is important to disable Touch on a stage that you are no longer using:
 *
 *      createjs.Touch.disable(stage);
 *
 * @class Touch
 * @static
 **/
	function Touch() {
		throw "Touch cannot be instantiated";
	}


// public static methods:
	/**
	 * Returns `true` if touch is supported in the current browser.
	 * @method isSupported
	 * @return {Boolean} Indicates whether touch is supported in the current browser.
	 * @static
	 **/
	Touch.isSupported = function() {
		return	!!(('ontouchstart' in window) // iOS & Android
			|| (window.navigator['msPointerEnabled'] && window.navigator['msMaxTouchPoints'] > 0) // IE10
			|| (window.navigator['pointerEnabled'] && window.navigator['maxTouchPoints'] > 0)); // IE11+
	};

	/**
	 * Enables touch interaction for the specified EaselJS {{#crossLink "Stage"}}{{/crossLink}}. Currently supports iOS
	 * (and compatible browsers, such as modern Android browsers), and IE10/11. Supports both single touch and
	 * multi-touch modes. Extends the EaselJS {{#crossLink "MouseEvent"}}{{/crossLink}} model, but without support for
	 * double click or over/out events. See the MouseEvent {{#crossLink "MouseEvent/pointerId:property"}}{{/crossLink}}
	 * for more information.
	 * @method enable
	 * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to enable touch on.
	 * @param {Boolean} [singleTouch=false] If `true`, only a single touch will be active at a time.
	 * @param {Boolean} [allowDefault=false] If `true`, then default gesture actions (ex. scrolling, zooming) will be
	 * allowed when the user is interacting with the target canvas.
	 * @return {Boolean} Returns `true` if touch was successfully enabled on the target stage.
	 * @static
	 **/
	Touch.enable = function(stage, singleTouch, allowDefault) {
		if (!stage || !stage.canvas || !Touch.isSupported()) { return false; }
		if (stage.__touch) { return true; }

		// inject required properties on stage:
		stage.__touch = {pointers:{}, multitouch:!singleTouch, preventDefault:!allowDefault, count:0};

		// note that in the future we may need to disable the standard mouse event model before adding
		// these to prevent duplicate calls. It doesn't seem to be an issue with iOS devices though.
		if ('ontouchstart' in window) { Touch._IOS_enable(stage); }
		else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_enable(stage); }
		return true;
	};

	/**
	 * Removes all listeners that were set up when calling `Touch.enable()` on a stage.
	 * @method disable
	 * @param {Stage} stage The {{#crossLink "Stage"}}{{/crossLink}} to disable touch on.
	 * @static
	 **/
	Touch.disable = function(stage) {
		if (!stage) { return; }
		if ('ontouchstart' in window) { Touch._IOS_disable(stage); }
		else if (window.navigator['msPointerEnabled'] || window.navigator["pointerEnabled"]) { Touch._IE_disable(stage); }
		
		delete stage.__touch;
	};


// Private static methods:
	/**
	 * @method _IOS_enable
	 * @protected
	 * @param {Stage} stage
	 * @static
	 **/
	Touch._IOS_enable = function(stage) {
		var canvas = stage.canvas;
		var f = stage.__touch.f = function(e) { Touch._IOS_handleEvent(stage,e); };
		canvas.addEventListener("touchstart", f, false);
		canvas.addEventListener("touchmove", f, false);
		canvas.addEventListener("touchend", f, false);
		canvas.addEventListener("touchcancel", f, false);
	};

	/**
	 * @method _IOS_disable
	 * @protected
	 * @param {Stage} stage
	 * @static
	 **/
	Touch._IOS_disable = function(stage) {
		var canvas = stage.canvas;
		if (!canvas) { return; }
		var f = stage.__touch.f;
		canvas.removeEventListener("touchstart", f, false);
		canvas.removeEventListener("touchmove", f, false);
		canvas.removeEventListener("touchend", f, false);
		canvas.removeEventListener("touchcancel", f, false);
	};

	/**
	 * @method _IOS_handleEvent
	 * @param {Stage} stage
	 * @param {Object} e The event to handle
	 * @protected
	 * @static
	 **/
	Touch._IOS_handleEvent = function(stage, e) {
		if (!stage) { return; }
		if (stage.__touch.preventDefault) { e.preventDefault&&e.preventDefault(); }
		var touches = e.changedTouches;
		var type = e.type;
		for (var i= 0,l=touches.length; i<l; i++) {
			var touch = touches[i];
			var id = touch.identifier;
			if (touch.target != stage.canvas) { continue; }

			if (type == "touchstart") {
				this._handleStart(stage, id, e, touch.pageX, touch.pageY);
			} else if (type == "touchmove") {
				this._handleMove(stage, id, e, touch.pageX, touch.pageY);
			} else if (type == "touchend" || type == "touchcancel") {
				this._handleEnd(stage, id, e);
			}
		}
	};

	/**
	 * @method _IE_enable
	 * @protected
	 * @param {Stage} stage
	 * @static
	 **/
	Touch._IE_enable = function(stage) {
		var canvas = stage.canvas;
		var f = stage.__touch.f = function(e) { Touch._IE_handleEvent(stage,e); };

		if (window.navigator["pointerEnabled"] === undefined) {
			canvas.addEventListener("MSPointerDown", f, false);
			window.addEventListener("MSPointerMove", f, false);
			window.addEventListener("MSPointerUp", f, false);
			window.addEventListener("MSPointerCancel", f, false);
			if (stage.__touch.preventDefault) { canvas.style.msTouchAction = "none"; }
		} else {
			canvas.addEventListener("pointerdown", f, false);
			window.addEventListener("pointermove", f, false);
			window.addEventListener("pointerup", f, false);
			window.addEventListener("pointercancel", f, false);
			if (stage.__touch.preventDefault) { canvas.style.touchAction = "none"; }

		}
		stage.__touch.activeIDs = {};
	};

	/**
	 * @method _IE_disable
	 * @protected
	 * @param {Stage} stage
	 * @static
	 **/
	Touch._IE_disable = function(stage) {
		var f = stage.__touch.f;

		if (window.navigator["pointerEnabled"] === undefined) {
			window.removeEventListener("MSPointerMove", f, false);
			window.removeEventListener("MSPointerUp", f, false);
			window.removeEventListener("MSPointerCancel", f, false);
			if (stage.canvas) {
				stage.canvas.removeEventListener("MSPointerDown", f, false);
			}
		} else {
			window.removeEventListener("pointermove", f, false);
			window.removeEventListener("pointerup", f, false);
			window.removeEventListener("pointercancel", f, false);
			if (stage.canvas) {
				stage.canvas.removeEventListener("pointerdown", f, false);
			}
		}
	};

	/**
	 * @method _IE_handleEvent
	 * @param {Stage} stage
	 * @param {Object} e The event to handle.
	 * @protected
	 * @static
	 **/
	Touch._IE_handleEvent = function(stage, e) {
		if (!stage) { return; }
		if (stage.__touch.preventDefault) { e.preventDefault && e.preventDefault(); }
		var type = e.type;
		var id = e.pointerId;
		var ids = stage.__touch.activeIDs;

		if (type == "MSPointerDown" || type == "pointerdown") {
			if (e.srcElement != stage.canvas) { return; }
			ids[id] = true;
			this._handleStart(stage, id, e, e.pageX, e.pageY);
		} else if (ids[id]) { // it's an id we're watching
			if (type == "MSPointerMove" || type == "pointermove") {
				this._handleMove(stage, id, e, e.pageX, e.pageY);
			} else if (type == "MSPointerUp" || type == "MSPointerCancel"
					|| type == "pointerup" || type == "pointercancel") {
				delete(ids[id]);
				this._handleEnd(stage, id, e);
			}
		}
	};

	/**
	 * @method _handleStart
	 * @param {Stage} stage
	 * @param {String|Number} id
	 * @param {Object} e
	 * @param {Number} x
	 * @param {Number} y
	 * @protected
	 **/
	Touch._handleStart = function(stage, id, e, x, y) {
		var props = stage.__touch;
		if (!props.multitouch && props.count) { return; }
		var ids = props.pointers;
		if (ids[id]) { return; }
		ids[id] = true;
		props.count++;
		stage._handlePointerDown(id, e, x, y);
	};

	/**
	 * @method _handleMove
	 * @param {Stage} stage
	 * @param {String|Number} id
	 * @param {Object} e
	 * @param {Number} x
	 * @param {Number} y
	 * @protected
	 **/
	Touch._handleMove = function(stage, id, e, x, y) {
		if (!stage.__touch.pointers[id]) { return; }
		stage._handlePointerMove(id, e, x, y);
	};

	/**
	 * @method _handleEnd
	 * @param {Stage} stage
	 * @param {String|Number} id
	 * @param {Object} e
	 * @protected
	 **/
	Touch._handleEnd = function(stage, id, e) {
		// TODO: cancel should be handled differently for proper UI (ex. an up would trigger a click, a cancel would more closely resemble an out).
		var props = stage.__touch;
		var ids = props.pointers;
		if (!ids[id]) { return; }
		props.count--;
		stage._handlePointerUp(id, e, true);
		delete(ids[id]);
	};


	createjs.Touch = Touch;
}());

//##############################################################################
// version.js
//##############################################################################

(function() {
	"use strict";

	/**
	 * Static class holding library specific information such as the version and buildDate of
	 * the library.
	 * @class EaselJS
	 **/
	var s = createjs.EaselJS = createjs.EaselJS || {};

	/**
	 * The version string for this release.
	 * @property version
	 * @type String
	 * @static
	 **/
	s.version = /*=version*/"0.8.2"; // injected by build process

	/**
	 * The build date for this release in UTC format.
	 * @property buildDate
	 * @type String
	 * @static
	 **/
	s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:34 GMT"; // injected by build process

})();

//##############################################################################
// version.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * Static class holding library specific information such as the version and buildDate of the library.
	 * @class PreloadJS
	 **/
	var s = createjs.PreloadJS = createjs.PreloadJS || {};

	/**
	 * The version string for this release.
	 * @property version
	 * @type {String}
	 * @static
	 **/
	s.version = /*=version*/"0.6.2"; // injected by build process

	/**
	 * The build date for this release in UTC format.
	 * @property buildDate
	 * @type {String}
	 * @static
	 **/
	s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:31 GMT"; // injected by build process

})();

//##############################################################################
// proxy.js
//##############################################################################

/**
 * Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the
 * createjs namespace directly.
 *
 * <h4>Example</h4>
 *
 *      myObject.addEventListener("change", createjs.proxy(myMethod, scope));
 *
 * @class Utility Methods
 * @main Utility Methods
 */

(function() {
	"use strict";

	/**
	 * A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a
	 * callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the
	 * method gets called in the correct scope.
	 *
	 * Additional arguments can be passed that will be applied to the function when it is called.
	 *
	 * <h4>Example</h4>
	 *
	 *      myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2));
	 *
	 *      function myHandler(arg1, arg2) {
	 *           // This gets called when myObject.myCallback is executed.
	 *      }
	 *
	 * @method proxy
	 * @param {Function} method The function to call
	 * @param {Object} scope The scope to call the method name on
	 * @param {mixed} [arg] * Arguments that are appended to the callback for additional params.
	 * @public
	 * @static
	 */
	createjs.proxy = function (method, scope) {
		var aArgs = Array.prototype.slice.call(arguments, 2);
		return function () {
			return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs));
		};
	}

}());

//##############################################################################
// ErrorEvent.js
//##############################################################################

(function() {
	"use strict";

	/**
	 * A general error {{#crossLink "Event"}}{{/crossLink}}, that describes an error that occurred, as well as any details.
	 * @class ErrorEvent
	 * @param {String} [title] The error title
	 * @param {String} [message] The error description
	 * @param {Object} [data] Additional error data
	 * @constructor
	 */
	function ErrorEvent(title, message, data) {
		this.Event_constructor("error");

		/**
		 * The short error title, which indicates the type of error that occurred.
		 * @property title
		 * @type String
		 */
		this.title = title;

		/**
		 * The verbose error message, containing details about the error.
		 * @property message
		 * @type String
		 */
		this.message = message;

		/**
		 * Additional data attached to an error.
		 * @property data
		 * @type {Object}
		 */
		this.data = data;
	}

	var p = createjs.extend(ErrorEvent, createjs.Event);

	p.clone = function() {
		return new createjs.ErrorEvent(this.title, this.message, this.data);
	};

	createjs.ErrorEvent = createjs.promote(ErrorEvent, "Event");

}());

//##############################################################################
// ProgressEvent.js
//##############################################################################

(function (scope) {
	"use strict";

	// constructor
	/**
	 * A CreateJS {{#crossLink "Event"}}{{/crossLink}} that is dispatched when progress changes.
	 * @class ProgressEvent
	 * @param {Number} loaded The amount that has been loaded. This can be any number relative to the total.
	 * @param {Number} [total=1] The total amount that will load. This will default to 1, so if the `loaded` value is
	 * a percentage (between 0 and 1), it can be omitted.
	 * @todo Consider having this event be a "fileprogress" event as well
	 * @constructor
	 */
	function ProgressEvent(loaded, total) {
		this.Event_constructor("progress");

		/**
		 * The amount that has been loaded (out of a total amount)
		 * @property loaded
		 * @type {Number}
		 */
		this.loaded = loaded;

		/**
		 * The total "size" of the load.
		 * @property total
		 * @type {Number}
		 * @default 1
		 */
		this.total = (total == null) ? 1 : total;

		/**
		 * The percentage (out of 1) that the load has been completed. This is calculated using `loaded/total`.
		 * @property progress
		 * @type {Number}
		 * @default 0
		 */
		this.progress = (total == 0) ? 0 : this.loaded / this.total;
	};

	var p = createjs.extend(ProgressEvent, createjs.Event);

	/**
	 * Returns a clone of the ProgressEvent instance.
	 * @method clone
	 * @return {ProgressEvent} a clone of the Event instance.
	 **/
	p.clone = function() {
		return new createjs.ProgressEvent(this.loaded, this.total);
	};

	createjs.ProgressEvent = createjs.promote(ProgressEvent, "Event");

}(window));

//##############################################################################
// json3.js
//##############################################################################

/*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */
;(function () {
  // Detect the `define` function exposed by asynchronous module loaders. The
  // strict `define` check is necessary for compatibility with `r.js`.
  var isLoader = typeof define === "function" && define.amd;

  // A set of types used to distinguish objects from primitives.
  var objectTypes = {
    "function": true,
    "object": true
  };

  // Detect the `exports` object exposed by CommonJS implementations.
  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;

  // Use the `global` object exposed by Node (including Browserify via
  // `insert-module-globals`), Narwhal, and Ringo as the default context,
  // and the `window` object in browsers. Rhino exports a `global` function
  // instead.
  var root = objectTypes[typeof window] && window || this,
      freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == "object" && global;

  if (freeGlobal && (freeGlobal["global"] === freeGlobal || freeGlobal["window"] === freeGlobal || freeGlobal["self"] === freeGlobal)) {
    root = freeGlobal;
  }

  // Public: Initializes JSON 3 using the given `context` object, attaching the
  // `stringify` and `parse` functions to the specified `exports` object.
  function runInContext(context, exports) {
    context || (context = root["Object"]());
    exports || (exports = root["Object"]());

    // Native constructor aliases.
    var Number = context["Number"] || root["Number"],
        String = context["String"] || root["String"],
        Object = context["Object"] || root["Object"],
        Date = context["Date"] || root["Date"],
        SyntaxError = context["SyntaxError"] || root["SyntaxError"],
        TypeError = context["TypeError"] || root["TypeError"],
        Math = context["Math"] || root["Math"],
        nativeJSON = context["JSON"] || root["JSON"];

    // Delegate to the native `stringify` and `parse` implementations.
    if (typeof nativeJSON == "object" && nativeJSON) {
      exports.stringify = nativeJSON.stringify;
      exports.parse = nativeJSON.parse;
    }

    // Convenience aliases.
    var objectProto = Object.prototype,
        getClass = objectProto.toString,
        isProperty, forEach, undef;

    // Test the `Date#getUTC*` methods. Based on work by @Yaffle.
    var isExtended = new Date(-3509827334573292);
    try {
      // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
      // results for certain dates in Opera >= 10.53.
      isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 &&
        // Safari < 2.0.2 stores the internal millisecond time value correctly,
        // but clips the values returned by the date methods to the range of
        // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
        isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
    } catch (exception) {}

    // Internal: Determines whether the native `JSON.stringify` and `parse`
    // implementations are spec-compliant. Based on work by Ken Snyder.
    function has(name) {
      if (has[name] !== undef) {
        // Return cached feature test result.
        return has[name];
      }
      var isSupported;
      if (name == "bug-string-char-index") {
        // IE <= 7 doesn't support accessing string characters using square
        // bracket notation. IE 8 only supports this for primitives.
        isSupported = "a"[0] != "a";
      } else if (name == "json") {
        // Indicates whether both `JSON.stringify` and `JSON.parse` are
        // supported.
        isSupported = has("json-stringify") && has("json-parse");
      } else {
        var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';
        // Test `JSON.stringify`.
        if (name == "json-stringify") {
          var stringify = exports.stringify, stringifySupported = typeof stringify == "function" && isExtended;
          if (stringifySupported) {
            // A test function object with a custom `toJSON` method.
            (value = function () {
              return 1;
            }).toJSON = value;
            try {
              stringifySupported =
                // Firefox 3.1b1 and b2 serialize string, number, and boolean
                // primitives as object literals.
                stringify(0) === "0" &&
                // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
                // literals.
                stringify(new Number()) === "0" &&
                stringify(new String()) == '""' &&
                // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
                // does not define a canonical JSON representation (this applies to
                // objects with `toJSON` properties as well, *unless* they are nested
                // within an object or array).
                stringify(getClass) === undef &&
                // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
                // FF 3.1b3 pass this test.
                stringify(undef) === undef &&
                // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
                // respectively, if the value is omitted entirely.
                stringify() === undef &&
                // FF 3.1b1, 2 throw an error if the given value is not a number,
                // string, array, object, Boolean, or `null` literal. This applies to
                // objects with custom `toJSON` methods as well, unless they are nested
                // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
                // methods entirely.
                stringify(value) === "1" &&
                stringify([value]) == "[1]" &&
                // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
                // `"[null]"`.
                stringify([undef]) == "[null]" &&
                // YUI 3.0.0b1 fails to serialize `null` literals.
                stringify(null) == "null" &&
                // FF 3.1b1, 2 halts serialization if an array contains a function:
                // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3
                // elides non-JSON values from objects and arrays, unless they
                // define custom `toJSON` methods.
                stringify([undef, getClass, null]) == "[null,null,null]" &&
                // Simple serialization test. FF 3.1b1 uses Unicode escape sequences
                // where character escape codes are expected (e.g., `\b` => `\u0008`).
                stringify({ "a": [value, true, false, null, "\x00\b\n\f\r\t"] }) == serialized &&
                // FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
                stringify(null, value) === "1" &&
                stringify([1, 2], null, 1) == "[\n 1,\n 2\n]" &&
                // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
                // serialize extended years.
                stringify(new Date(-8.64e15)) == '"-271821-04-20T00:00:00.000Z"' &&
                // The milliseconds are optional in ES 5, but required in 5.1.
                stringify(new Date(8.64e15)) == '"+275760-09-13T00:00:00.000Z"' &&
                // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
                // four-digit years instead of six-digit years. Credits: @Yaffle.
                stringify(new Date(-621987552e5)) == '"-000001-01-01T00:00:00.000Z"' &&
                // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
                // values less than 1000. Credits: @Yaffle.
                stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
            } catch (exception) {
              stringifySupported = false;
            }
          }
          isSupported = stringifySupported;
        }
        // Test `JSON.parse`.
        if (name == "json-parse") {
          var parse = exports.parse;
          if (typeof parse == "function") {
            try {
              // FF 3.1b1, b2 will throw an exception if a bare literal is provided.
              // Conforming implementations should also coerce the initial argument to
              // a string prior to parsing.
              if (parse("0") === 0 && !parse(false)) {
                // Simple parsing test.
                value = parse(serialized);
                var parseSupported = value["a"].length == 5 && value["a"][0] === 1;
                if (parseSupported) {
                  try {
                    // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
                    parseSupported = !parse('"\t"');
                  } catch (exception) {}
                  if (parseSupported) {
                    try {
                      // FF 4.0 and 4.0.1 allow leading `+` signs and leading
                      // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow
                      // certain octal literals.
                      parseSupported = parse("01") !== 1;
                    } catch (exception) {}
                  }
                  if (parseSupported) {
                    try {
                      // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal
                      // points. These environments, along with FF 3.1b1 and 2,
                      // also allow trailing commas in JSON objects and arrays.
                      parseSupported = parse("1.") !== 1;
                    } catch (exception) {}
                  }
                }
              }
            } catch (exception) {
              parseSupported = false;
            }
          }
          isSupported = parseSupported;
        }
      }
      return has[name] = !!isSupported;
    }

    if (!has("json")) {
      // Common `[[Class]]` name aliases.
      var functionClass = "[object Function]",
          dateClass = "[object Date]",
          numberClass = "[object Number]",
          stringClass = "[object String]",
          arrayClass = "[object Array]",
          booleanClass = "[object Boolean]";

      // Detect incomplete support for accessing string characters by index.
      var charIndexBuggy = has("bug-string-char-index");

      // Define additional utility methods if the `Date` methods are buggy.
      if (!isExtended) {
        var floor = Math.floor;
        // A mapping between the months of the year and the number of days between
        // January 1st and the first of the respective month.
        var Months = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
        // Internal: Calculates the number of days between the Unix epoch and the
        // first day of the given month.
        var getDay = function (year, month) {
          return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
        };
      }

      // Internal: Determines if a property is a direct property of the given
      // object. Delegates to the native `Object#hasOwnProperty` method.
      if (!(isProperty = objectProto.hasOwnProperty)) {
        isProperty = function (property) {
          var members = {}, constructor;
          if ((members.__proto__ = null, members.__proto__ = {
            // The *proto* property cannot be set multiple times in recent
            // versions of Firefox and SeaMonkey.
            "toString": 1
          }, members).toString != getClass) {
            // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
            // supports the mutable *proto* property.
            isProperty = function (property) {
              // Capture and break the object's prototype chain (see section 8.6.2
              // of the ES 5.1 spec). The parenthesized expression prevents an
              // unsafe transformation by the Closure Compiler.
              var original = this.__proto__, result = property in (this.__proto__ = null, this);
              // Restore the original prototype chain.
              this.__proto__ = original;
              return result;
            };
          } else {
            // Capture a reference to the top-level `Object` constructor.
            constructor = members.constructor;
            // Use the `constructor` property to simulate `Object#hasOwnProperty` in
            // other environments.
            isProperty = function (property) {
              var parent = (this.constructor || constructor).prototype;
              return property in this && !(property in parent && this[property] === parent[property]);
            };
          }
          members = null;
          return isProperty.call(this, property);
        };
      }

      // Internal: Normalizes the `for...in` iteration algorithm across
      // environments. Each enumerated key is yielded to a `callback` function.
      forEach = function (object, callback) {
        var size = 0, Properties, members, property;

        // Tests for bugs in the current environment's `for...in` algorithm. The
        // `valueOf` property inherits the non-enumerable flag from
        // `Object.prototype` in older versions of IE, Netscape, and Mozilla.
        (Properties = function () {
          this.valueOf = 0;
        }).prototype.valueOf = 0;

        // Iterate over a new instance of the `Properties` class.
        members = new Properties();
        for (property in members) {
          // Ignore all properties inherited from `Object.prototype`.
          if (isProperty.call(members, property)) {
            size++;
          }
        }
        Properties = members = null;

        // Normalize the iteration algorithm.
        if (!size) {
          // A list of non-enumerable properties inherited from `Object.prototype`.
          members = ["valueOf", "toString", "toLocaleString", "propertyIsEnumerable", "isPrototypeOf", "hasOwnProperty", "constructor"];
          // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
          // properties.
          forEach = function (object, callback) {
            var isFunction = getClass.call(object) == functionClass, property, length;
            var hasProperty = !isFunction && typeof object.constructor != "function" && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty;
            for (property in object) {
              // Gecko <= 1.0 enumerates the `prototype` property of functions under
              // certain conditions; IE does not.
              if (!(isFunction && property == "prototype") && hasProperty.call(object, property)) {
                callback(property);
              }
            }
            // Manually invoke the callback for each non-enumerable property.
            for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property));
          };
        } else if (size == 2) {
          // Safari <= 2.0.4 enumerates shadowed properties twice.
          forEach = function (object, callback) {
            // Create a set of iterated properties.
            var members = {}, isFunction = getClass.call(object) == functionClass, property;
            for (property in object) {
              // Store each property name to prevent double enumeration. The
              // `prototype` property of functions is not enumerated due to cross-
              // environment inconsistencies.
              if (!(isFunction && property == "prototype") && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
                callback(property);
              }
            }
          };
        } else {
          // No bugs detected; use the standard `for...in` algorithm.
          forEach = function (object, callback) {
            var isFunction = getClass.call(object) == functionClass, property, isConstructor;
            for (property in object) {
              if (!(isFunction && property == "prototype") && isProperty.call(object, property) && !(isConstructor = property === "constructor")) {
                callback(property);
              }
            }
            // Manually invoke the callback for the `constructor` property due to
            // cross-environment inconsistencies.
            if (isConstructor || isProperty.call(object, (property = "constructor"))) {
              callback(property);
            }
          };
        }
        return forEach(object, callback);
      };

      // Public: Serializes a JavaScript `value` as a JSON string. The optional
      // `filter` argument may specify either a function that alters how object and
      // array members are serialized, or an array of strings and numbers that
      // indicates which properties should be serialized. The optional `width`
      // argument may be either a string or number that specifies the indentation
      // level of the output.
      if (!has("json-stringify")) {
        // Internal: A map of control characters and their escaped equivalents.
        var Escapes = {
          92: "\\\\",
          34: '\\"',
          8: "\\b",
          12: "\\f",
          10: "\\n",
          13: "\\r",
          9: "\\t"
        };

        // Internal: Converts `value` into a zero-padded string such that its
        // length is at least equal to `width`. The `width` must be <= 6.
        var leadingZeroes = "000000";
        var toPaddedString = function (width, value) {
          // The `|| 0` expression is necessary to work around a bug in
          // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
          return (leadingZeroes + (value || 0)).slice(-width);
        };

        // Internal: Double-quotes a string `value`, replacing all ASCII control
        // characters (characters with code unit values between 0 and 31) with
        // their escaped equivalents. This is an implementation of the
        // `Quote(value)` operation defined in ES 5.1 section 15.12.3.
        var unicodePrefix = "\\u00";
        var quote = function (value) {
          var result = '"', index = 0, length = value.length, useCharIndex = !charIndexBuggy || length > 10;
          var symbols = useCharIndex && (charIndexBuggy ? value.split("") : value);
          for (; index < length; index++) {
            var charCode = value.charCodeAt(index);
            // If the character is a control character, append its Unicode or
            // shorthand escape sequence; otherwise, append the character as-is.
            switch (charCode) {
              case 8: case 9: case 10: case 12: case 13: case 34: case 92:
                result += Escapes[charCode];
                break;
              default:
                if (charCode < 32) {
                  result += unicodePrefix + toPaddedString(2, charCode.toString(16));
                  break;
                }
                result += useCharIndex ? symbols[index] : value.charAt(index);
            }
          }
          return result + '"';
        };

        // Internal: Recursively serializes an object. Implements the
        // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
        var serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
          var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result;
          try {
            // Necessary for host object support.
            value = object[property];
          } catch (exception) {}
          if (typeof value == "object" && value) {
            className = getClass.call(value);
            if (className == dateClass && !isProperty.call(value, "toJSON")) {
              if (value > -1 / 0 && value < 1 / 0) {
                // Dates are serialized according to the `Date#toJSON` method
                // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
                // for the ISO 8601 date time string format.
                if (getDay) {
                  // Manually compute the year, month, date, hours, minutes,
                  // seconds, and milliseconds if the `getUTC*` methods are
                  // buggy. Adapted from @Yaffle's `date-shim` project.
                  date = floor(value / 864e5);
                  for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
                  for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
                  date = 1 + date - getDay(year, month);
                  // The `time` value specifies the time within the day (see ES
                  // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
                  // to compute `A modulo B`, as the `%` operator does not
                  // correspond to the `modulo` operation for negative numbers.
                  time = (value % 864e5 + 864e5) % 864e5;
                  // The hours, minutes, seconds, and milliseconds are obtained by
                  // decomposing the time within the day. See section 15.9.1.10.
                  hours = floor(time / 36e5) % 24;
                  minutes = floor(time / 6e4) % 60;
                  seconds = floor(time / 1e3) % 60;
                  milliseconds = time % 1e3;
                } else {
                  year = value.getUTCFullYear();
                  month = value.getUTCMonth();
                  date = value.getUTCDate();
                  hours = value.getUTCHours();
                  minutes = value.getUTCMinutes();
                  seconds = value.getUTCSeconds();
                  milliseconds = value.getUTCMilliseconds();
                }
                // Serialize extended years correctly.
                value = (year <= 0 || year >= 1e4 ? (year < 0 ? "-" : "+") + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) +
                  "-" + toPaddedString(2, month + 1) + "-" + toPaddedString(2, date) +
                  // Months, dates, hours, minutes, and seconds should have two
                  // digits; milliseconds should have three.
                  "T" + toPaddedString(2, hours) + ":" + toPaddedString(2, minutes) + ":" + toPaddedString(2, seconds) +
                  // Milliseconds are optional in ES 5.0, but required in 5.1.
                  "." + toPaddedString(3, milliseconds) + "Z";
              } else {
                value = null;
              }
            } else if (typeof value.toJSON == "function" && ((className != numberClass && className != stringClass && className != arrayClass) || isProperty.call(value, "toJSON"))) {
              // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
              // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
              // ignores all `toJSON` methods on these objects unless they are
              // defined directly on an instance.
              value = value.toJSON(property);
            }
          }
          if (callback) {
            // If a replacement function was provided, call it to obtain the value
            // for serialization.
            value = callback.call(object, property, value);
          }
          if (value === null) {
            return "null";
          }
          className = getClass.call(value);
          if (className == booleanClass) {
            // Booleans are represented literally.
            return "" + value;
          } else if (className == numberClass) {
            // JSON numbers must be finite. `Infinity` and `NaN` are serialized as
            // `"null"`.
            return value > -1 / 0 && value < 1 / 0 ? "" + value : "null";
          } else if (className == stringClass) {
            // Strings are double-quoted and escaped.
            return quote("" + value);
          }
          // Recursively serialize objects and arrays.
          if (typeof value == "object") {
            // Check for cyclic structures. This is a linear search; performance
            // is inversely proportional to the number of unique nested objects.
            for (length = stack.length; length--;) {
              if (stack[length] === value) {
                // Cyclic structures cannot be serialized by `JSON.stringify`.
                throw TypeError();
              }
            }
            // Add the object to the stack of traversed objects.
            stack.push(value);
            results = [];
            // Save the current indentation level and indent one additional level.
            prefix = indentation;
            indentation += whitespace;
            if (className == arrayClass) {
              // Recursively serialize array elements.
              for (index = 0, length = value.length; index < length; index++) {
                element = serialize(index, value, callback, properties, whitespace, indentation, stack);
                results.push(element === undef ? "null" : element);
              }
              result = results.length ? (whitespace ? "[\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "]" : ("[" + results.join(",") + "]")) : "[]";
            } else {
              // Recursively serialize object members. Members are selected from
              // either a user-specified list of property names, or the object
              // itself.
              forEach(properties || value, function (property) {
                var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
                if (element !== undef) {
                  // According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
                  // is not the empty string, let `member` {quote(property) + ":"}
                  // be the concatenation of `member` and the `space` character."
                  // The "`space` character" refers to the literal space
                  // character, not the `space` {width} argument provided to
                  // `JSON.stringify`.
                  results.push(quote(property) + ":" + (whitespace ? " " : "") + element);
                }
              });
              result = results.length ? (whitespace ? "{\n" + indentation + results.join(",\n" + indentation) + "\n" + prefix + "}" : ("{" + results.join(",") + "}")) : "{}";
            }
            // Remove the object from the traversed object stack.
            stack.pop();
            return result;
          }
        };

        // Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
        exports.stringify = function (source, filter, width) {
          var whitespace, callback, properties, className;
          if (objectTypes[typeof filter] && filter) {
            if ((className = getClass.call(filter)) == functionClass) {
              callback = filter;
            } else if (className == arrayClass) {
              // Convert the property names array into a makeshift set.
              properties = {};
              for (var index = 0, length = filter.length, value; index < length; value = filter[index++], ((className = getClass.call(value)), className == stringClass || className == numberClass) && (properties[value] = 1));
            }
          }
          if (width) {
            if ((className = getClass.call(width)) == numberClass) {
              // Convert the `width` to an integer and create a string containing
              // `width` number of space characters.
              if ((width -= width % 1) > 0) {
                for (whitespace = "", width > 10 && (width = 10); whitespace.length < width; whitespace += " ");
              }
            } else if (className == stringClass) {
              whitespace = width.length <= 10 ? width : width.slice(0, 10);
            }
          }
          // Opera <= 7.54u2 discards the values associated with empty string keys
          // (`""`) only if they are used directly within an object member list
          // (e.g., `!("" in { "": 1})`).
          return serialize("", (value = {}, value[""] = source, value), callback, properties, whitespace, "", []);
        };
      }

      // Public: Parses a JSON source string.
      if (!has("json-parse")) {
        var fromCharCode = String.fromCharCode;

        // Internal: A map of escaped control characters and their unescaped
        // equivalents.
        var Unescapes = {
          92: "\\",
          34: '"',
          47: "/",
          98: "\b",
          116: "\t",
          110: "\n",
          102: "\f",
          114: "\r"
        };

        // Internal: Stores the parser state.
        var Index, Source;

        // Internal: Resets the parser state and throws a `SyntaxError`.
        var abort = function () {
          Index = Source = null;
          throw SyntaxError();
        };

        // Internal: Returns the next token, or `"$"` if the parser has reached
        // the end of the source string. A token may be a string, number, `null`
        // literal, or Boolean literal.
        var lex = function () {
          var source = Source, length = source.length, value, begin, position, isSigned, charCode;
          while (Index < length) {
            charCode = source.charCodeAt(Index);
            switch (charCode) {
              case 9: case 10: case 13: case 32:
                // Skip whitespace tokens, including tabs, carriage returns, line
                // feeds, and space characters.
                Index++;
                break;
              case 123: case 125: case 91: case 93: case 58: case 44:
                // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at
                // the current position.
                value = charIndexBuggy ? source.charAt(Index) : source[Index];
                Index++;
                return value;
              case 34:
                // `"` delimits a JSON string; advance to the next character and
                // begin parsing the string. String tokens are prefixed with the
                // sentinel `@` character to distinguish them from punctuators and
                // end-of-string tokens.
                for (value = "@", Index++; Index < length;) {
                  charCode = source.charCodeAt(Index);
                  if (charCode < 32) {
                    // Unescaped ASCII control characters (those with a code unit
                    // less than the space character) are not permitted.
                    abort();
                  } else if (charCode == 92) {
                    // A reverse solidus (`\`) marks the beginning of an escaped
                    // control character (including `"`, `\`, and `/`) or Unicode
                    // escape sequence.
                    charCode = source.charCodeAt(++Index);
                    switch (charCode) {
                      case 92: case 34: case 47: case 98: case 116: case 110: case 102: case 114:
                        // Revive escaped control characters.
                        value += Unescapes[charCode];
                        Index++;
                        break;
                      case 117:
                        // `\u` marks the beginning of a Unicode escape sequence.
                        // Advance to the first character and validate the
                        // four-digit code point.
                        begin = ++Index;
                        for (position = Index + 4; Index < position; Index++) {
                          charCode = source.charCodeAt(Index);
                          // A valid sequence comprises four hexdigits (case-
                          // insensitive) that form a single hexadecimal value.
                          if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) {
                            // Invalid Unicode escape sequence.
                            abort();
                          }
                        }
                        // Revive the escaped character.
                        value += fromCharCode("0x" + source.slice(begin, Index));
                        break;
                      default:
                        // Invalid escape sequence.
                        abort();
                    }
                  } else {
                    if (charCode == 34) {
                      // An unescaped double-quote character marks the end of the
                      // string.
                      break;
                    }
                    charCode = source.charCodeAt(Index);
                    begin = Index;
                    // Optimize for the common case where a string is valid.
                    while (charCode >= 32 && charCode != 92 && charCode != 34) {
                      charCode = source.charCodeAt(++Index);
                    }
                    // Append the string as-is.
                    value += source.slice(begin, Index);
                  }
                }
                if (source.charCodeAt(Index) == 34) {
                  // Advance to the next character and return the revived string.
                  Index++;
                  return value;
                }
                // Unterminated string.
                abort();
              default:
                // Parse numbers and literals.
                begin = Index;
                // Advance past the negative sign, if one is specified.
                if (charCode == 45) {
                  isSigned = true;
                  charCode = source.charCodeAt(++Index);
                }
                // Parse an integer or floating-point value.
                if (charCode >= 48 && charCode <= 57) {
                  // Leading zeroes are interpreted as octal literals.
                  if (charCode == 48 && ((charCode = source.charCodeAt(Index + 1)), charCode >= 48 && charCode <= 57)) {
                    // Illegal octal literal.
                    abort();
                  }
                  isSigned = false;
                  // Parse the integer component.
                  for (; Index < length && ((charCode = source.charCodeAt(Index)), charCode >= 48 && charCode <= 57); Index++);
                  // Floats cannot contain a leading decimal point; however, this
                  // case is already accounted for by the parser.
                  if (source.charCodeAt(Index) == 46) {
                    position = ++Index;
                    // Parse the decimal component.
                    for (; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++);
                    if (position == Index) {
                      // Illegal trailing decimal.
                      abort();
                    }
                    Index = position;
                  }
                  // Parse exponents. The `e` denoting the exponent is
                  // case-insensitive.
                  charCode = source.charCodeAt(Index);
                  if (charCode == 101 || charCode == 69) {
                    charCode = source.charCodeAt(++Index);
                    // Skip past the sign following the exponent, if one is
                    // specified.
                    if (charCode == 43 || charCode == 45) {
                      Index++;
                    }
                    // Parse the exponential component.
                    for (position = Index; position < length && ((charCode = source.charCodeAt(position)), charCode >= 48 && charCode <= 57); position++);
                    if (position == Index) {
                      // Illegal empty exponent.
                      abort();
                    }
                    Index = position;
                  }
                  // Coerce the parsed value to a JavaScript number.
                  return +source.slice(begin, Index);
                }
                // A negative sign may only precede numbers.
                if (isSigned) {
                  abort();
                }
                // `true`, `false`, and `null` literals.
                if (source.slice(Index, Index + 4) == "true") {
                  Index += 4;
                  return true;
                } else if (source.slice(Index, Index + 5) == "false") {
                  Index += 5;
                  return false;
                } else if (source.slice(Index, Index + 4) == "null") {
                  Index += 4;
                  return null;
                }
                // Unrecognized token.
                abort();
            }
          }
          // Return the sentinel `$` character if the parser has reached the end
          // of the source string.
          return "$";
        };

        // Internal: Parses a JSON `value` token.
        var get = function (value) {
          var results, hasMembers;
          if (value == "$") {
            // Unexpected end of input.
            abort();
          }
          if (typeof value == "string") {
            if ((charIndexBuggy ? value.charAt(0) : value[0]) == "@") {
              // Remove the sentinel `@` character.
              return value.slice(1);
            }
            // Parse object and array literals.
            if (value == "[") {
              // Parses a JSON array, returning a new JavaScript array.
              results = [];
              for (;; hasMembers || (hasMembers = true)) {
                value = lex();
                // A closing square bracket marks the end of the array literal.
                if (value == "]") {
                  break;
                }
                // If the array literal contains elements, the current token
                // should be a comma separating the previous element from the
                // next.
                if (hasMembers) {
                  if (value == ",") {
                    value = lex();
                    if (value == "]") {
                      // Unexpected trailing `,` in array literal.
                      abort();
                    }
                  } else {
                    // A `,` must separate each array element.
                    abort();
                  }
                }
                // Elisions and leading commas are not permitted.
                if (value == ",") {
                  abort();
                }
                results.push(get(value));
              }
              return results;
            } else if (value == "{") {
              // Parses a JSON object, returning a new JavaScript object.
              results = {};
              for (;; hasMembers || (hasMembers = true)) {
                value = lex();
                // A closing curly brace marks the end of the object literal.
                if (value == "}") {
                  break;
                }
                // If the object literal contains members, the current token
                // should be a comma separator.
                if (hasMembers) {
                  if (value == ",") {
                    value = lex();
                    if (value == "}") {
                      // Unexpected trailing `,` in object literal.
                      abort();
                    }
                  } else {
                    // A `,` must separate each object member.
                    abort();
                  }
                }
                // Leading commas are not permitted, object property names must be
                // double-quoted strings, and a `:` must separate each property
                // name and value.
                if (value == "," || typeof value != "string" || (charIndexBuggy ? value.charAt(0) : value[0]) != "@" || lex() != ":") {
                  abort();
                }
                results[value.slice(1)] = get(lex());
              }
              return results;
            }
            // Unexpected token encountered.
            abort();
          }
          return value;
        };

        // Internal: Updates a traversed object member.
        var update = function (source, property, callback) {
          var element = walk(source, property, callback);
          if (element === undef) {
            delete source[property];
          } else {
            source[property] = element;
          }
        };

        // Internal: Recursively traverses a parsed JSON object, invoking the
        // `callback` function for each value. This is an implementation of the
        // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
        var walk = function (source, property, callback) {
          var value = source[property], length;
          if (typeof value == "object" && value) {
            // `forEach` can't be used to traverse an array in Opera <= 8.54
            // because its `Object#hasOwnProperty` implementation returns `false`
            // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`).
            if (getClass.call(value) == arrayClass) {
              for (length = value.length; length--;) {
                update(value, length, callback);
              }
            } else {
              forEach(value, function (property) {
                update(value, property, callback);
              });
            }
          }
          return callback.call(source, property, value);
        };

        // Public: `JSON.parse`. See ES 5.1 section 15.12.2.
        exports.parse = function (source, callback) {
          var result, value;
          Index = 0;
          Source = "" + source;
          result = get(lex());
          // If a JSON string contains multiple tokens, it is invalid.
          if (lex() != "$") {
            abort();
          }
          // Reset the parser state.
          Index = Source = null;
          return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[""] = result, value), "", callback) : result;
        };
      }
    }

    exports["runInContext"] = runInContext;
    return exports;
  }

  if (freeExports && !isLoader) {
    // Export for CommonJS environments.
    runInContext(root, freeExports);
  } else {
    // Export for web browsers and JavaScript engines.
    var nativeJSON = root.JSON,
        previousJSON = root["JSON3"],
        isRestored = false;

    var JSON3 = runInContext(root, (root["JSON3"] = {
      // Public: Restores the original value of the global `JSON` object and
      // returns a reference to the `JSON3` object.
      "noConflict": function () {
        if (!isRestored) {
          isRestored = true;
          root.JSON = nativeJSON;
          root["JSON3"] = previousJSON;
          nativeJSON = previousJSON = null;
        }
        return JSON3;
      }
    }));

    root.JSON = {
      "parse": JSON3.parse,
      "stringify": JSON3.stringify
    };
  }

  // Export for asynchronous module loaders.
  if (isLoader) {
    define(function () {
      return JSON3;
    });
  }
}).call(this);

//##############################################################################
// DomUtils.js
//##############################################################################

(function () {

	/**
	 * A few utilities for interacting with the dom.
	 * @class DomUtils
	 */
	var s = {};

	s.appendToHead = function (el) {
		s.getHead().appendChild(el)
	}

	s.getHead = function () {
		return document.head || document.getElementsByTagName("head")[0];
	}

	s.getBody = function () {
		return document.body || document.getElementsByTagName("body")[0];
	}

	createjs.DomUtils = s;

}());

//##############################################################################
// DataUtils.js
//##############################################################################

(function () {

	/**
	 * A few data utilities for formatting different data types.
	 * @class DataUtils
	 */
	var s = {};

	// static methods
	/**
	 * Parse XML using the DOM. This is required when preloading XML or SVG.
	 * @method parseXML
	 * @param {String} text The raw text or XML that is loaded by XHR.
	 * @param {String} type The mime type of the XML. Use "text/xml" for XML, and  "image/svg+xml" for SVG parsing.
	 * @return {XML} An XML document
	 * @static
	 */
	s.parseXML = function (text, type) {
		var xml = null;
		// CocoonJS does not support XML parsing with either method.

		// Most browsers will use DOMParser
		// IE fails on certain SVG files, so we have a fallback below.
		try {
			if (window.DOMParser) {
				var parser = new DOMParser();
				xml = parser.parseFromString(text, type);
			}
		} catch (e) {
		}

		// Fallback for IE support.
		if (!xml) {
			try {
				xml = new ActiveXObject("Microsoft.XMLDOM");
				xml.async = false;
				xml.loadXML(text);
			} catch (e) {
				xml = null;
			}
		}

		return xml;
	};

	/**
	 * Parse a string into an Object.
	 * @method parseJSON
	 * @param {String} value The loaded JSON string
	 * @returns {Object} A JavaScript object.
	 */
	s.parseJSON = function (value) {
		if (value == null) {
			return null;
		}

		try {
			return JSON.parse(value);
		} catch (e) {
			// TODO; Handle this with a custom error?
			throw e;
		}
	};

	createjs.DataUtils = s;

}());

//##############################################################################
// LoadItem.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * All loaders accept an item containing the properties defined in this class. If a raw object is passed instead,
	 * it will not be affected, but it must contain at least a {{#crossLink "src:property"}}{{/crossLink}} property. A
	 * string path or HTML tag is also acceptable, but it will be automatically converted to a LoadItem using the
	 * {{#crossLink "create"}}{{/crossLink}} method by {{#crossLink "AbstractLoader"}}{{/crossLink}}
	 * @class LoadItem
	 * @constructor
	 * @since 0.6.0
	 */
	function LoadItem() {
		/**
		 * The source of the file that is being loaded. This property is <b>required</b>. The source can either be a
		 * string (recommended), or an HTML tag.
		 * This can also be an object, but in that case it has to include a type and be handled by a plugin.
		 * @property src
		 * @type {String}
		 * @default null
		 */
		this.src = null;

		/**
		 * The type file that is being loaded. The type of the file is usually inferred by the extension, but can also
		 * be set manually. This is helpful in cases where a file does not have an extension.
		 * @property type
		 * @type {String}
		 * @default null
		 */
		this.type = null;

		/**
		 * A string identifier which can be used to reference the loaded object. If none is provided, this will be
		 * automatically set to the {{#crossLink "src:property"}}{{/crossLink}}.
		 * @property id
		 * @type {String}
		 * @default null
		 */
		this.id = null;

		/**
		 * Determines if a manifest will maintain the order of this item, in relation to other items in the manifest
		 * that have also set the `maintainOrder` property to `true`. This only applies when the max connections has
		 * been set above 1 (using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}). Everything with this
		 * property set to `false` will finish as it is loaded. Ordered items are combined with script tags loading in
		 * order when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}} is set to `true`.
		 * @property maintainOrder
		 * @type {Boolean}
		 * @default false
		 */
		this.maintainOrder = false;

		/**
		 * A callback used by JSONP requests that defines what global method to call when the JSONP content is loaded.
		 * @property callback
		 * @type {String}
		 * @default null
		 */
		this.callback = null;

		/**
		 * An arbitrary data object, which is included with the loaded object.
		 * @property data
		 * @type {Object}
		 * @default null
		 */
		this.data = null;

		/**
		 * The request method used for HTTP calls. Both {{#crossLink "AbstractLoader/GET:property"}}{{/crossLink}} or
		 * {{#crossLink "AbstractLoader/POST:property"}}{{/crossLink}} request types are supported, and are defined as
		 * constants on {{#crossLink "AbstractLoader"}}{{/crossLink}}.
		 * @property method
		 * @type {String}
		 * @default get
		 */
		this.method = createjs.LoadItem.GET;

		/**
		 * An object hash of name/value pairs to send to the server.
		 * @property values
		 * @type {Object}
		 * @default null
		 */
		this.values = null;

		/**
		 * An object hash of headers to attach to an XHR request. PreloadJS will automatically attach some default
		 * headers when required, including "Origin", "Content-Type", and "X-Requested-With". You may override the
		 * default headers by including them in your headers object.
		 * @property headers
		 * @type {Object}
		 * @default null
		 */
		this.headers = null;

		/**
		 * Enable credentials for XHR requests.
		 * @property withCredentials
		 * @type {Boolean}
		 * @default false
		 */
		this.withCredentials = false;

		/**
		 * Set the mime type of XHR-based requests. This is automatically set to "text/plain; charset=utf-8" for text
		 * based files (json, xml, text, css, js).
		 * @property mimeType
		 * @type {String}
		 * @default null
		 */
		this.mimeType = null;

		/**
		 * Sets the crossOrigin attribute for CORS-enabled images loading cross-domain.
		 * @property crossOrigin
		 * @type {boolean}
		 * @default Anonymous
		 */
		this.crossOrigin = null;

		/**
		 * The duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
		 * (level one) loading, as XHR (level 2) provides its own timeout event.
		 * @property loadTimeout
		 * @type {Number}
		 * @default 8000 (8 seconds)
		 */
		this.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
	};

	var p = LoadItem.prototype = {};
	var s = LoadItem;

	/**
	 * Default duration in milliseconds to wait before a request times out. This only applies to tag-based and and XHR
	 * (level one) loading, as XHR (level 2) provides its own timeout event.
	 * @property LOAD_TIMEOUT_DEFAULT
	 * @type {number}
	 * @static
	 */
	s.LOAD_TIMEOUT_DEFAULT = 8000;

	/**
	 * Create a LoadItem.
	 * <ul>
	 *     <li>String-based items are converted to a LoadItem with a populated {{#crossLink "src:property"}}{{/crossLink}}.</li>
	 *     <li>LoadItem instances are returned as-is</li>
	 *     <li>Objects are returned with any needed properties added</li>
	 * </ul>
	 * @method create
	 * @param {LoadItem|String|Object} value The load item value
	 * @returns {LoadItem|Object}
	 * @static
	 */
	s.create = function (value) {
		if (typeof value == "string") {
			var item = new LoadItem();
			item.src = value;
			return item;
		} else if (value instanceof s) {
			return value;
		} else if (value instanceof Object && value.src) {
			if (value.loadTimeout == null) {
				value.loadTimeout = s.LOAD_TIMEOUT_DEFAULT;
			}
			return value;
		} else {
			throw new Error("Type not recognized.");
		}
	};

	/**
	 * Provides a chainable shortcut method for setting a number of properties on the instance.
	 *
	 * <h4>Example</h4>
	 *
	 *      var loadItem = new createjs.LoadItem().set({src:"image.png", maintainOrder:true});
	 *
	 * @method set
	 * @param {Object} props A generic object containing properties to copy to the LoadItem instance.
	 * @return {LoadItem} Returns the instance the method is called on (useful for chaining calls.)
	*/
	p.set = function(props) {
		for (var n in props) { this[n] = props[n]; }
		return this;
	};

	createjs.LoadItem = s;

}());

//##############################################################################
// RequestUtils.js
//##############################################################################

(function () {

	/**
	 * Utilities that assist with parsing load items, and determining file types, etc.
	 * @class RequestUtils
	 */
	var s = {};

	/**
	 * The Regular Expression used to test file URLS for an absolute path.
	 * @property ABSOLUTE_PATH
	 * @type {RegExp}
	 * @static
	 */
	s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;

	/**
	 * The Regular Expression used to test file URLS for a relative path.
	 * @property RELATIVE_PATH
	 * @type {RegExp}
	 * @static
	 */
	s.RELATIVE_PATT = (/^[./]*?\//i);

	/**
	 * The Regular Expression used to test file URLS for an extension. Note that URIs must already have the query string
	 * removed.
	 * @property EXTENSION_PATT
	 * @type {RegExp}
	 * @static
	 */
	s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;

	/**
	 * Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
	 * <ul>
	 *     <li>If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
	 *     `//networkPath`)</li>
	 *     <li>If the path is relative. Relative paths start with `../` or `/path` (or similar)</li>
	 *     <li>The file extension. This is determined by the filename with an extension. Query strings are dropped, and
	 *     the file path is expected to follow the format `name.ext`.</li>
	 * </ul>
	 * @method parseURI
	 * @param {String} path
	 * @returns {Object} An Object with an `absolute` and `relative` Boolean values, as well as an optional 'extension`
	 * property, which is the lowercase extension.
	 * @static
	 */
	s.parseURI = function (path) {
		var info = {absolute: false, relative: false};
		if (path == null) { return info; }

		// Drop the query string
		var queryIndex = path.indexOf("?");
		if (queryIndex > -1) {
			path = path.substr(0, queryIndex);
		}

		// Absolute
		var match;
		if (s.ABSOLUTE_PATT.test(path)) {
			info.absolute = true;

			// Relative
		} else if (s.RELATIVE_PATT.test(path)) {
			info.relative = true;
		}

		// Extension
		if (match = path.match(s.EXTENSION_PATT)) {
			info.extension = match[1].toLowerCase();
		}
		return info;
	};

	/**
	 * Formats an object into a query string for either a POST or GET request.
	 * @method formatQueryString
	 * @param {Object} data The data to convert to a query string.
	 * @param {Array} [query] Existing name/value pairs to append on to this query.
	 * @static
	 */
	s.formatQueryString = function (data, query) {
		if (data == null) {
			throw new Error('You must specify data.');
		}
		var params = [];
		for (var n in data) {
			params.push(n + '=' + escape(data[n]));
		}
		if (query) {
			params = params.concat(query);
		}
		return params.join('&');
	};

	/**
	 * A utility method that builds a file path using a source and a data object, and formats it into a new path.
	 * @method buildPath
	 * @param {String} src The source path to add values to.
	 * @param {Object} [data] Object used to append values to this request as a query string. Existing parameters on the
	 * path will be preserved.
	 * @returns {string} A formatted string that contains the path and the supplied parameters.
	 * @static
	 */
	s.buildPath = function (src, data) {
		if (data == null) {
			return src;
		}

		var query = [];
		var idx = src.indexOf('?');

		if (idx != -1) {
			var q = src.slice(idx + 1);
			query = query.concat(q.split('&'));
		}

		if (idx != -1) {
			return src.slice(0, idx) + '?' + this.formatQueryString(data, query);
		} else {
			return src + '?' + this.formatQueryString(data, query);
		}
	};

	/**
	 * @method isCrossDomain
	 * @param {LoadItem|Object} item A load item with a `src` property.
	 * @return {Boolean} If the load item is loading from a different domain than the current location.
	 * @static
	 */
	s.isCrossDomain = function (item) {
		var target = document.createElement("a");
		target.href = item.src;

		var host = document.createElement("a");
		host.href = location.href;

		var crossdomain = (target.hostname != "") &&
						  (target.port != host.port ||
						   target.protocol != host.protocol ||
						   target.hostname != host.hostname);
		return crossdomain;
	};

	/**
	 * @method isLocal
	 * @param {LoadItem|Object} item A load item with a `src` property
	 * @return {Boolean} If the load item is loading from the "file:" protocol. Assume that the host must be local as
	 * well.
	 * @static
	 */
	s.isLocal = function (item) {
		var target = document.createElement("a");
		target.href = item.src;
		return target.hostname == "" && target.protocol == "file:";
	};

	/**
	 * Determine if a specific type should be loaded as a binary file. Currently, only images and items marked
	 * specifically as "binary" are loaded as binary. Note that audio is <b>not</b> a binary type, as we can not play
	 * back using an audio tag if it is loaded as binary. Plugins can change the item type to binary to ensure they get
	 * a binary result to work with. Binary files are loaded using XHR2. Types are defined as static constants on
	 * {{#crossLink "AbstractLoader"}}{{/crossLink}}.
	 * @method isBinary
	 * @param {String} type The item type.
	 * @return {Boolean} If the specified type is binary.
	 * @static
	 */
	s.isBinary = function (type) {
		switch (type) {
			case createjs.AbstractLoader.IMAGE:
			case createjs.AbstractLoader.BINARY:
				return true;
			default:
				return false;
		}
	};

	/**
	 * Check if item is a valid HTMLImageElement
	 * @method isImageTag
	 * @param {Object} item
	 * @returns {Boolean}
	 * @static
	 */
	s.isImageTag = function(item) {
		return item instanceof HTMLImageElement;
	};

	/**
	 * Check if item is a valid HTMLAudioElement
	 * @method isAudioTag
	 * @param {Object} item
	 * @returns {Boolean}
	 * @static
	 */
	s.isAudioTag = function(item) {
		if (window.HTMLAudioElement) {
			return item instanceof HTMLAudioElement;
		} else {
			return false;
		}
	};

	/**
	 * Check if item is a valid HTMLVideoElement
	 * @method isVideoTag
	 * @param {Object} item
	 * @returns {Boolean}
	 * @static
	 */
	s.isVideoTag = function(item) {
		if (window.HTMLVideoElement) {
			return item instanceof HTMLVideoElement;
		} else {
			return false;
		}
	};

	/**
	 * Determine if a specific type is a text-based asset, and should be loaded as UTF-8.
	 * @method isText
	 * @param {String} type The item type.
	 * @return {Boolean} If the specified type is text.
	 * @static
	 */
	s.isText = function (type) {
		switch (type) {
			case createjs.AbstractLoader.TEXT:
			case createjs.AbstractLoader.JSON:
			case createjs.AbstractLoader.MANIFEST:
			case createjs.AbstractLoader.XML:
			case createjs.AbstractLoader.CSS:
			case createjs.AbstractLoader.SVG:
			case createjs.AbstractLoader.JAVASCRIPT:
			case createjs.AbstractLoader.SPRITESHEET:
				return true;
			default:
				return false;
		}
	};

	/**
	 * Determine the type of the object using common extensions. Note that the type can be passed in with the load item
	 * if it is an unusual extension.
	 * @method getTypeByExtension
	 * @param {String} extension The file extension to use to determine the load type.
	 * @return {String} The determined load type (for example, <code>AbstractLoader.IMAGE</code>). Will return `null` if
	 * the type can not be determined by the extension.
	 * @static
	 */
	s.getTypeByExtension = function (extension) {
		if (extension == null) {
			return createjs.AbstractLoader.TEXT;
		}

		switch (extension.toLowerCase()) {
			case "jpeg":
			case "jpg":
			case "gif":
			case "png":
			case "webp":
			case "bmp":
				return createjs.AbstractLoader.IMAGE;
			case "ogg":
			case "mp3":
			case "webm":
				return createjs.AbstractLoader.SOUND;
			case "mp4":
			case "webm":
			case "ts":
				return createjs.AbstractLoader.VIDEO;
			case "json":
				return createjs.AbstractLoader.JSON;
			case "xml":
				return createjs.AbstractLoader.XML;
			case "css":
				return createjs.AbstractLoader.CSS;
			case "js":
				return createjs.AbstractLoader.JAVASCRIPT;
			case 'svg':
				return createjs.AbstractLoader.SVG;
			default:
				return createjs.AbstractLoader.TEXT;
		}
	};

	createjs.RequestUtils = s;

}());

//##############################################################################
// AbstractLoader.js
//##############################################################################

(function () {
	"use strict";

// constructor
	/**
	 * The base loader, which defines all the generic methods, properties, and events. All loaders extend this class,
	 * including the {{#crossLink "LoadQueue"}}{{/crossLink}}.
	 * @class AbstractLoader
	 * @param {LoadItem|object|string} loadItem The item to be loaded.
	 * @param {Boolean} [preferXHR] Determines if the LoadItem should <em>try</em> and load using XHR, or take a
	 * tag-based approach, which can be better in cross-domain situations. Not all loaders can load using one or the
	 * other, so this is a suggested directive.
	 * @param {String} [type] The type of loader. Loader types are defined as constants on the AbstractLoader class,
	 * such as {{#crossLink "IMAGE:property"}}{{/crossLink}}, {{#crossLink "CSS:property"}}{{/crossLink}}, etc.
	 * @extends EventDispatcher
	 */
	function AbstractLoader(loadItem, preferXHR, type) {
		this.EventDispatcher_constructor();

		// public properties
		/**
		 * If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
		 * used for loading do not pile up resulting in more than one `complete` {{#crossLink "Event"}}{{/crossLink}}.
		 * @property loaded
		 * @type {Boolean}
		 * @default false
		 */
		this.loaded = false;

		/**
		 * Determine if the loader was canceled. Canceled loads will not fire complete events. Note that this property
		 * is readonly, so {{#crossLink "LoadQueue"}}{{/crossLink}} queues should be closed using {{#crossLink "LoadQueue/close"}}{{/crossLink}}
		 * instead.
		 * @property canceled
		 * @type {Boolean}
		 * @default false
		 * @readonly
		 */
		this.canceled = false;

		/**
		 * The current load progress (percentage) for this item. This will be a number between 0 and 1.
		 *
		 * <h4>Example</h4>
		 *
		 *     var queue = new createjs.LoadQueue();
		 *     queue.loadFile("largeImage.png");
		 *     queue.on("progress", function() {
		 *         console.log("Progress:", queue.progress, event.progress);
		 *     });
		 *
		 * @property progress
		 * @type {Number}
		 * @default 0
		 */
		this.progress = 0;

		/**
		 * The type of item this loader will load. See {{#crossLink "AbstractLoader"}}{{/crossLink}} for a full list of
		 * supported types.
		 * @property type
		 * @type {String}
		 */
		this.type = type;

		/**
		 * A formatter function that converts the loaded raw result into the final result. For example, the JSONLoader
		 * converts a string of text into a JavaScript object. Not all loaders have a resultFormatter, and this property
		 * can be overridden to provide custom formatting.
		 *
		 * Optionally, a resultFormatter can return a callback function in cases where the formatting needs to be
		 * asynchronous, such as creating a new image. The callback function is passed 2 parameters, which are callbacks
		 * to handle success and error conditions in the resultFormatter. Note that the resultFormatter method is
		 * called in the current scope, as well as the success and error callbacks.
		 *
		 * <h4>Example asynchronous resultFormatter</h4>
		 *
		 * 	function _formatResult(loader) {
		 * 		return function(success, error) {
		 * 			if (errorCondition) { error(errorDetailEvent); }
		 * 			success(result);
		 * 		}
		 * 	}
		 * @property resultFormatter
		 * @type {Function}
		 * @default null
		 */
		this.resultFormatter = null;

		// protected properties
		/**
		 * The {{#crossLink "LoadItem"}}{{/crossLink}} this loader represents. Note that this is null in a {{#crossLink "LoadQueue"}}{{/crossLink}},
		 * but will be available on loaders such as {{#crossLink "XMLLoader"}}{{/crossLink}} and {{#crossLink "ImageLoader"}}{{/crossLink}}.
		 * @property _item
		 * @type {LoadItem|Object}
		 * @private
		 */
		if (loadItem) {
			this._item = createjs.LoadItem.create(loadItem);
		} else {
			this._item = null;
		}

		/**
		 * Whether the loader will try and load content using XHR (true) or HTML tags (false).
		 * @property _preferXHR
		 * @type {Boolean}
		 * @private
		 */
		this._preferXHR = preferXHR;

		/**
		 * The loaded result after it is formatted by an optional {{#crossLink "resultFormatter"}}{{/crossLink}}. For
		 * items that are not formatted, this will be the same as the {{#crossLink "_rawResult:property"}}{{/crossLink}}.
		 * The result is accessed using the {{#crossLink "getResult"}}{{/crossLink}} method.
		 * @property _result
		 * @type {Object|String}
		 * @private
		 */
		this._result = null;

		/**
		 * The loaded result before it is formatted. The rawResult is accessed using the {{#crossLink "getResult"}}{{/crossLink}}
		 * method, and passing `true`.
		 * @property _rawResult
		 * @type {Object|String}
		 * @private
		 */
		this._rawResult = null;

		/**
		 * A list of items that loaders load behind the scenes. This does not include the main item the loader is
		 * responsible for loading. Examples of loaders that have sub-items include the {{#crossLink "SpriteSheetLoader"}}{{/crossLink}} and
		 * {{#crossLink "ManifestLoader"}}{{/crossLink}}.
		 * @property _loadItems
		 * @type {null}
		 * @protected
		 */
		this._loadedItems = null;

		/**
		 * The attribute the items loaded using tags use for the source.
		 * @type {string}
		 * @default null
		 * @private
		 */
		this._tagSrcAttribute = null;

		/**
		 * An HTML tag (or similar) that a loader may use to load HTML content, such as images, scripts, etc.
		 * @property _tag
		 * @type {Object}
		 * @private
		 */
		this._tag = null;
	};

	var p = createjs.extend(AbstractLoader, createjs.EventDispatcher);
	var s = AbstractLoader;

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


	/**
	 * Defines a POST request, use for a method value when loading data.
	 * @property POST
	 * @type {string}
	 * @default post
	 * @static
	 */
	s.POST = "POST";

	/**
	 * Defines a GET request, use for a method value when loading data.
	 * @property GET
	 * @type {string}
	 * @default get
	 * @static
	 */
	s.GET = "GET";

	/**
	 * The preload type for generic binary types. Note that images are loaded as binary files when using XHR.
	 * @property BINARY
	 * @type {String}
	 * @default binary
	 * @static
	 * @since 0.6.0
	 */
	s.BINARY = "binary";

	/**
	 * The preload type for css files. CSS files are loaded using a &lt;link&gt; when loaded with XHR, or a
	 * &lt;style&gt; tag when loaded with tags.
	 * @property CSS
	 * @type {String}
	 * @default css
	 * @static
	 * @since 0.6.0
	 */
	s.CSS = "css";

	/**
	 * The preload type for image files, usually png, gif, or jpg/jpeg. Images are loaded into an &lt;image&gt; tag.
	 * @property IMAGE
	 * @type {String}
	 * @default image
	 * @static
	 * @since 0.6.0
	 */
	s.IMAGE = "image";

	/**
	 * The preload type for javascript files, usually with the "js" file extension. JavaScript files are loaded into a
	 * &lt;script&gt; tag.
	 *
	 * Since version 0.4.1+, due to how tag-loaded scripts work, all JavaScript files are automatically injected into
	 * the body of the document to maintain parity between XHR and tag-loaded scripts. In version 0.4.0 and earlier,
	 * only tag-loaded scripts are injected.
	 * @property JAVASCRIPT
	 * @type {String}
	 * @default javascript
	 * @static
	 * @since 0.6.0
	 */
	s.JAVASCRIPT = "javascript";

	/**
	 * The preload type for json files, usually with the "json" file extension. JSON data is loaded and parsed into a
	 * JavaScript object. Note that if a `callback` is present on the load item, the file will be loaded with JSONP,
	 * no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to, and the JSON
	 * must contain a matching wrapper function.
	 * @property JSON
	 * @type {String}
	 * @default json
	 * @static
	 * @since 0.6.0
	 */
	s.JSON = "json";

	/**
	 * The preload type for jsonp files, usually with the "json" file extension. JSON data is loaded and parsed into a
	 * JavaScript object. You are required to pass a callback parameter that matches the function wrapper in the JSON.
	 * Note that JSONP will always be used if there is a callback present, no matter what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}}
	 * property is set to.
	 * @property JSONP
	 * @type {String}
	 * @default jsonp
	 * @static
	 * @since 0.6.0
	 */
	s.JSONP = "jsonp";

	/**
	 * The preload type for json-based manifest files, usually with the "json" file extension. The JSON data is loaded
	 * and parsed into a JavaScript object. PreloadJS will then look for a "manifest" property in the JSON, which is an
	 * Array of files to load, following the same format as the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
	 * method. If a "callback" is specified on the manifest object, then it will be loaded using JSONP instead,
	 * regardless of what the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property is set to.
	 * @property MANIFEST
	 * @type {String}
	 * @default manifest
	 * @static
	 * @since 0.6.0
	 */
	s.MANIFEST = "manifest";

	/**
	 * The preload type for sound files, usually mp3, ogg, or wav. When loading via tags, audio is loaded into an
	 * &lt;audio&gt; tag.
	 * @property SOUND
	 * @type {String}
	 * @default sound
	 * @static
	 * @since 0.6.0
	 */
	s.SOUND = "sound";

	/**
	 * The preload type for video files, usually mp4, ts, or ogg. When loading via tags, video is loaded into an
	 * &lt;video&gt; tag.
	 * @property VIDEO
	 * @type {String}
	 * @default video
	 * @static
	 * @since 0.6.0
	 */
	s.VIDEO = "video";

	/**
	 * The preload type for SpriteSheet files. SpriteSheet files are JSON files that contain string image paths.
	 * @property SPRITESHEET
	 * @type {String}
	 * @default spritesheet
	 * @static
	 * @since 0.6.0
	 */
	s.SPRITESHEET = "spritesheet";

	/**
	 * The preload type for SVG files.
	 * @property SVG
	 * @type {String}
	 * @default svg
	 * @static
	 * @since 0.6.0
	 */
	s.SVG = "svg";

	/**
	 * The preload type for text files, which is also the default file type if the type can not be determined. Text is
	 * loaded as raw text.
	 * @property TEXT
	 * @type {String}
	 * @default text
	 * @static
	 * @since 0.6.0
	 */
	s.TEXT = "text";

	/**
	 * The preload type for xml files. XML is loaded into an XML document.
	 * @property XML
	 * @type {String}
	 * @default xml
	 * @static
	 * @since 0.6.0
	 */
	s.XML = "xml";

// Events
	/**
	 * The {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when the overall progress changes. Prior to
	 * version 0.6.0, this was just a regular {{#crossLink "Event"}}{{/crossLink}}.
	 * @event progress
	 * @since 0.3.0
	 */

	/**
	 * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a load starts.
	 * @event loadstart
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @since 0.3.1
	 */

	/**
	 * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the entire queue has been loaded.
	 * @event complete
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @since 0.3.0
	 */

	/**
	 * The {{#crossLink "ErrorEvent"}}{{/crossLink}} that is fired when the loader encounters an error. If the error was
	 * encountered by a file, the event will contain the item that caused the error. Prior to version 0.6.0, this was
	 * just a regular {{#crossLink "Event"}}{{/crossLink}}.
	 * @event error
	 * @since 0.3.0
	 */

	/**
	 * The {{#crossLink "Event"}}{{/crossLink}} that is fired when the loader encounters an internal file load error.
	 * This enables loaders to maintain internal queues, and surface file load errors.
	 * @event fileerror
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The even type ("fileerror")
	 * @param {LoadItem|object} The item that encountered the error
	 * @since 0.6.0
	 */

	/**
	 * The {{#crossLink "Event"}}{{/crossLink}} that is fired when a loader internally loads a file. This enables
	 * loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}} to maintain internal {{#crossLink "LoadQueue"}}{{/crossLink}}s
	 * and notify when they have loaded a file. The {{#crossLink "LoadQueue"}}{{/crossLink}} class dispatches a
	 * slightly different {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event.
	 * @event fileload
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type ("fileload")
	 * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
	 * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
	 * object will contain that value as a `src` property.
	 * @param {Object} result The HTML tag or parsed result of the loaded item.
	 * @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
	 * to a usable object.
	 * @since 0.6.0
	 */

	/**
	 * The {{#crossLink "Event"}}{{/crossLink}} that is fired after the internal request is created, but before a load.
	 * This allows updates to the loader for specific loading needs, such as binary or XHR image loading.
	 * @event initialize
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type ("initialize")
	 * @param {AbstractLoader} loader The loader that has been initialized.
	 */


	/**
	 * Get a reference to the manifest item that is loaded by this loader. In some cases this will be the value that was
	 * passed into {{#crossLink "LoadQueue"}}{{/crossLink}} using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
	 * {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. However if only a String path was passed in, then it will
	 * be a {{#crossLink "LoadItem"}}{{/crossLink}}.
	 * @method getItem
	 * @return {Object} The manifest item that this loader is responsible for loading.
	 * @since 0.6.0
	 */
	p.getItem = function () {
		return this._item;
	};

	/**
	 * Get a reference to the content that was loaded by the loader (only available after the {{#crossLink "complete:event"}}{{/crossLink}}
	 * event is dispatched.
	 * @method getResult
	 * @param {Boolean} [raw=false] Determines if the returned result will be the formatted content, or the raw loaded
	 * data (if it exists).
	 * @return {Object}
	 * @since 0.6.0
	 */
	p.getResult = function (raw) {
		return raw ? this._rawResult : this._result;
	};

	/**
	 * Return the `tag` this object creates or uses for loading.
	 * @method getTag
	 * @return {Object} The tag instance
	 * @since 0.6.0
	 */
	p.getTag = function () {
		return this._tag;
	};

	/**
	 * Set the `tag` this item uses for loading.
	 * @method setTag
	 * @param {Object} tag The tag instance
	 * @since 0.6.0
	 */
	p.setTag = function(tag) {
	  this._tag = tag;
	};

	/**
	 * Begin loading the item. This method is required when using a loader by itself.
	 *
	 * <h4>Example</h4>
	 *
	 *      var queue = new createjs.LoadQueue();
	 *      queue.on("complete", handleComplete);
	 *      queue.loadManifest(fileArray, false); // Note the 2nd argument that tells the queue not to start loading yet
	 *      queue.load();
	 *
	 * @method load
	 */
	p.load = function () {
		this._createRequest();

		this._request.on("complete", this, this);
		this._request.on("progress", this, this);
		this._request.on("loadStart", this, this);
		this._request.on("abort", this, this);
		this._request.on("timeout", this, this);
		this._request.on("error", this, this);

		var evt = new createjs.Event("initialize");
		evt.loader = this._request;
		this.dispatchEvent(evt);

		this._request.load();
	};

	/**
	 * Close the the item. This will stop any open requests (although downloads using HTML tags may still continue in
	 * the background), but events will not longer be dispatched.
	 * @method cancel
	 */
	p.cancel = function () {
		this.canceled = true;
		this.destroy();
	};

	/**
	 * Clean up the loader.
	 * @method destroy
	 */
	p.destroy = function() {
		if (this._request) {
			this._request.removeAllEventListeners();
			this._request.destroy();
		}

		this._request = null;

		this._item = null;
		this._rawResult = null;
		this._result = null;

		this._loadItems = null;

		this.removeAllEventListeners();
	};

	/**
	 * Get any items loaded internally by the loader. The enables loaders such as {{#crossLink "ManifestLoader"}}{{/crossLink}}
	 * to expose items it loads internally.
	 * @method getLoadedItems
	 * @return {Array} A list of the items loaded by the loader.
	 * @since 0.6.0
	 */
	p.getLoadedItems = function () {
		return this._loadedItems;
	};


	// Private methods
	/**
	 * Create an internal request used for loading. By default, an {{#crossLink "XHRRequest"}}{{/crossLink}} or
	 * {{#crossLink "TagRequest"}}{{/crossLink}} is created, depending on the value of {{#crossLink "preferXHR:property"}}{{/crossLink}}.
	 * Other loaders may override this to use different request types, such as {{#crossLink "ManifestLoader"}}{{/crossLink}},
	 * which uses {{#crossLink "JSONLoader"}}{{/crossLink}} or {{#crossLink "JSONPLoader"}}{{/crossLink}} under the hood.
	 * @method _createRequest
	 * @protected
	 */
	p._createRequest = function() {
		if (!this._preferXHR) {
			this._request = new createjs.TagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
		} else {
			this._request = new createjs.XHRRequest(this._item);
		}
	};

	/**
	 * Create the HTML tag used for loading. This method does nothing by default, and needs to be implemented
	 * by loaders that require tag loading.
	 * @method _createTag
	 * @param {String} src The tag source
	 * @return {HTMLElement} The tag that was created
	 * @protected
	 */
	p._createTag = function(src) { return null; };

	/**
	 * Dispatch a loadstart {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/loadstart:event"}}{{/crossLink}}
	 * event for details on the event payload.
	 * @method _sendLoadStart
	 * @protected
	 */
	p._sendLoadStart = function () {
		if (this._isCanceled()) { return; }
		this.dispatchEvent("loadstart");
	};

	/**
	 * Dispatch a {{#crossLink "ProgressEvent"}}{{/crossLink}}.
	 * @method _sendProgress
	 * @param {Number | Object} value The progress of the loaded item, or an object containing <code>loaded</code>
	 * and <code>total</code> properties.
	 * @protected
	 */
	p._sendProgress = function (value) {
		if (this._isCanceled()) { return; }
		var event = null;
		if (typeof(value) == "number") {
			this.progress = value;
			event = new createjs.ProgressEvent(this.progress);
		} else {
			event = value;
			this.progress = value.loaded / value.total;
			event.progress = this.progress;
			if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
		}
		this.hasEventListener("progress") && this.dispatchEvent(event);
	};

	/**
	 * Dispatch a complete {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}} event
	 * @method _sendComplete
	 * @protected
	 */
	p._sendComplete = function () {
		if (this._isCanceled()) { return; }

		this.loaded = true;

		var event = new createjs.Event("complete");
		event.rawResult = this._rawResult;

		if (this._result != null) {
			event.result = this._result;
		}

		this.dispatchEvent(event);
	};

	/**
	 * Dispatch an error {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
	 * event for details on the event payload.
	 * @method _sendError
	 * @param {ErrorEvent} event The event object containing specific error properties.
	 * @protected
	 */
	p._sendError = function (event) {
		if (this._isCanceled() || !this.hasEventListener("error")) { return; }
		if (event == null) {
			event = new createjs.ErrorEvent("PRELOAD_ERROR_EMPTY"); // TODO: Populate error
		}
		this.dispatchEvent(event);
	};

	/**
	 * Determine if the load has been canceled. This is important to ensure that method calls or asynchronous events
	 * do not cause issues after the queue has been cleaned up.
	 * @method _isCanceled
	 * @return {Boolean} If the loader has been canceled.
	 * @protected
	 */
	p._isCanceled = function () {
		if (window.createjs == null || this.canceled) {
			return true;
		}
		return false;
	};

	/**
	 * A custom result formatter function, which is called just before a request dispatches its complete event. Most
	 * loader types already have an internal formatter, but this can be user-overridden for custom formatting. The
	 * formatted result will be available on Loaders using {{#crossLink "getResult"}}{{/crossLink}}, and passing `true`.
	 * @property resultFormatter
	 * @type Function
	 * @return {Object} The formatted result
	 * @since 0.6.0
	 */
	p.resultFormatter = null;

	/**
	 * Handle events from internal requests. By default, loaders will handle, and redispatch the necessary events, but
	 * this method can be overridden for custom behaviours.
	 * @method handleEvent
	 * @param {Event} event The event that the internal request dispatches.
	 * @protected
	 * @since 0.6.0
	 */
	p.handleEvent = function (event) {
		switch (event.type) {
			case "complete":
				this._rawResult = event.target._response;
				var result = this.resultFormatter && this.resultFormatter(this);
				if (result instanceof Function) {
					result.call(this,
							createjs.proxy(this._resultFormatSuccess, this),
							createjs.proxy(this._resultFormatFailed, this)
					);
				} else {
					this._result =  result || this._rawResult;
					this._sendComplete();
				}
				break;
			case "progress":
				this._sendProgress(event);
				break;
			case "error":
				this._sendError(event);
				break;
			case "loadstart":
				this._sendLoadStart();
				break;
			case "abort":
			case "timeout":
				if (!this._isCanceled()) {
					this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_" + event.type.toUpperCase() + "_ERROR"));
				}
				break;
		}
	};

	/**
	 * The "success" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
	 * functions.
	 * @method _resultFormatSuccess
	 * @param {Object} result The formatted result
	 * @private
	 */
	p._resultFormatSuccess = function (result) {
		this._result = result;
		this._sendComplete();
	};

	/**
	 * The "error" callback passed to {{#crossLink "AbstractLoader/resultFormatter"}}{{/crossLink}} asynchronous
	 * functions.
	 * @method _resultFormatSuccess
	 * @param {Object} error The error event
	 * @private
	 */
	p._resultFormatFailed = function (event) {
		this._sendError(event);
	};

	/**
	 * @method buildPath
	 * @protected
	 * @deprecated Use the {{#crossLink "RequestUtils"}}{{/crossLink}} method {{#crossLink "RequestUtils/buildPath"}}{{/crossLink}}
	 * instead.
	 */
	p.buildPath = function (src, data) {
		return createjs.RequestUtils.buildPath(src, data);
	};

	/**
	 * @method toString
	 * @return {String} a string representation of the instance.
	 */
	p.toString = function () {
		return "[PreloadJS AbstractLoader]";
	};

	createjs.AbstractLoader = createjs.promote(AbstractLoader, "EventDispatcher");

}());

//##############################################################################
// AbstractMediaLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * The AbstractMediaLoader is a base class that handles some of the shared methods and properties of loaders that
	 * handle HTML media elements, such as Video and Audio.
	 * @class AbstractMediaLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @param {String} type The type of media to load. Usually "video" or "audio".
	 * @extends AbstractLoader
	 * @constructor
	 */
	function AbstractMediaLoader(loadItem, preferXHR, type) {
		this.AbstractLoader_constructor(loadItem, preferXHR, type);

		// public properties
		this.resultFormatter = this._formatResult;

		// protected properties
		this._tagSrcAttribute = "src";

        this.on("initialize", this._updateXHR, this);
	};

	var p = createjs.extend(AbstractMediaLoader, createjs.AbstractLoader);

	// static properties
	// public methods
	p.load = function () {
		// TagRequest will handle most of this, but Sound / Video need a few custom properties, so just handle them here.
		if (!this._tag) {
			this._tag = this._createTag(this._item.src);
		}

		this._tag.preload = "auto";
		this._tag.load();

		this.AbstractLoader_load();
	};

	// protected methods
	/**
	 * Creates a new tag for loading if it doesn't exist yet.
	 * @method _createTag
	 * @private
	 */
	p._createTag = function () {};


	p._createRequest = function() {
		if (!this._preferXHR) {
			this._request = new createjs.MediaTagRequest(this._item, this._tag || this._createTag(), this._tagSrcAttribute);
		} else {
			this._request = new createjs.XHRRequest(this._item);
		}
	};

    // protected methods
    /**
     * Before the item loads, set its mimeType and responseType.
     * @property _updateXHR
     * @param {Event} event
     * @private
     */
    p._updateXHR = function (event) {
        // Only exists for XHR
        if (event.loader.setResponseType) {
            event.loader.setResponseType("blob");
        }
    };

	/**
	 * The result formatter for media files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {HTMLVideoElement|HTMLAudioElement}
	 * @private
	 */
	p._formatResult = function (loader) {
		this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
		this._tag.onstalled = null;
		if (this._preferXHR) {
            var URL = window.URL || window.webkitURL;
            var result = loader.getResult(true);

			loader.getTag().src = URL.createObjectURL(result);
		}
		return loader.getTag();
	};

	createjs.AbstractMediaLoader = createjs.promote(AbstractMediaLoader, "AbstractLoader");

}());

//##############################################################################
// AbstractRequest.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * A base class for actual data requests, such as {{#crossLink "XHRRequest"}}{{/crossLink}}, {{#crossLink "TagRequest"}}{{/crossLink}},
	 * and {{#crossLink "MediaRequest"}}{{/crossLink}}. PreloadJS loaders will typically use a data loader under the
	 * hood to get data.
	 * @class AbstractRequest
	 * @param {LoadItem} item
	 * @constructor
	 */
	var AbstractRequest = function (item) {
		this._item = item;
	};

	var p = createjs.extend(AbstractRequest, createjs.EventDispatcher);

	// public methods
	/**
	 * Begin a load.
	 * @method load
	 */
	p.load =  function() {};

	/**
	 * Clean up a request.
	 * @method destroy
	 */
	p.destroy = function() {};

	/**
	 * Cancel an in-progress request.
	 * @method cancel
	 */
	p.cancel = function() {};

	createjs.AbstractRequest = createjs.promote(AbstractRequest, "EventDispatcher");

}());

//##############################################################################
// TagRequest.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * An {{#crossLink "AbstractRequest"}}{{/crossLink}} that loads HTML tags, such as images and scripts.
	 * @class TagRequest
	 * @param {LoadItem} loadItem
	 * @param {HTMLElement} tag
	 * @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
	 */
	function TagRequest(loadItem, tag, srcAttribute) {
		this.AbstractRequest_constructor(loadItem);

		// protected properties
		/**
		 * The HTML tag instance that is used to load.
		 * @property _tag
		 * @type {HTMLElement}
		 * @protected
		 */
		this._tag = tag;

		/**
		 * The tag attribute that specifies the source, such as "src", "href", etc.
		 * @property _tagSrcAttribute
		 * @type {String}
		 * @protected
		 */
		this._tagSrcAttribute = srcAttribute;

		/**
		 * A method closure used for handling the tag load event.
		 * @property _loadedHandler
		 * @type {Function}
		 * @private
		 */
		this._loadedHandler = createjs.proxy(this._handleTagComplete, this);

		/**
		 * Determines if the element was added to the DOM automatically by PreloadJS, so it can be cleaned up after.
		 * @property _addedToDOM
		 * @type {Boolean}
		 * @private
		 */
		this._addedToDOM = false;

		/**
		 * Determines what the tags initial style.visibility was, so we can set it correctly after a load.
		 *
		 * @type {null}
		 * @private
		 */
		this._startTagVisibility = null;
	};

	var p = createjs.extend(TagRequest, createjs.AbstractRequest);

	// public methods
	p.load = function () {
		this._tag.onload = createjs.proxy(this._handleTagComplete, this);
		this._tag.onreadystatechange = createjs.proxy(this._handleReadyStateChange, this);
		this._tag.onerror = createjs.proxy(this._handleError, this);

		var evt = new createjs.Event("initialize");
		evt.loader = this._tag;

		this.dispatchEvent(evt);

		this._hideTag();

		this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);

		this._tag[this._tagSrcAttribute] = this._item.src;

		// wdg:: Append the tag AFTER setting the src, or SVG loading on iOS will fail.
		if (this._tag.parentNode == null) {
			window.document.body.appendChild(this._tag);
			this._addedToDOM = true;
		}
	};

	p.destroy = function() {
		this._clean();
		this._tag = null;

		this.AbstractRequest_destroy();
	};

	// private methods
	/**
	 * Handle the readyStateChange event from a tag. We need this in place of the `onload` callback (mainly SCRIPT
	 * and LINK tags), but other cases may exist.
	 * @method _handleReadyStateChange
	 * @private
	 */
	p._handleReadyStateChange = function () {
		clearTimeout(this._loadTimeout);
		// This is strictly for tags in browsers that do not support onload.
		var tag = this._tag;

		// Complete is for old IE support.
		if (tag.readyState == "loaded" || tag.readyState == "complete") {
			this._handleTagComplete();
		}
	};

	/**
	 * Handle any error events from the tag.
	 * @method _handleError
	 * @protected
	 */
	p._handleError = function() {
		this._clean();
		this.dispatchEvent("error");
	};

	/**
	 * Handle the tag's onload callback.
	 * @method _handleTagComplete
	 * @private
	 */
	p._handleTagComplete = function () {
		this._rawResult = this._tag;
		this._result = this.resultFormatter && this.resultFormatter(this) || this._rawResult;

		this._clean();
		this._showTag();

		this.dispatchEvent("complete");
	};

	/**
	 * The tag request has not loaded within the time specified in loadTimeout.
	 * @method _handleError
	 * @param {Object} event The XHR error event.
	 * @private
	 */
	p._handleTimeout = function () {
		this._clean();
		this.dispatchEvent(new createjs.Event("timeout"));
	};

	/**
	 * Remove event listeners, but don't destroy the request object
	 * @method _clean
	 * @private
	 */
	p._clean = function() {
		this._tag.onload = null;
		this._tag.onreadystatechange = null;
		this._tag.onerror = null;
		if (this._addedToDOM && this._tag.parentNode != null) {
			this._tag.parentNode.removeChild(this._tag);
		}
		clearTimeout(this._loadTimeout);
	};

	p._hideTag = function() {
		this._startTagVisibility = this._tag.style.visibility;
		this._tag.style.visibility = "hidden";
	};

	p._showTag = function() {
		this._tag.style.visibility = this._startTagVisibility;
	};

	/**
	 * Handle a stalled audio event. The main place this happens is with HTMLAudio in Chrome when playing back audio
	 * that is already in a load, but not complete.
	 * @method _handleStalled
	 * @private
	 */
	p._handleStalled = function () {
		//Ignore, let the timeout take care of it. Sometimes its not really stopped.
	};

	createjs.TagRequest = createjs.promote(TagRequest, "AbstractRequest");

}());

//##############################################################################
// MediaTagRequest.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * An {{#crossLink "TagRequest"}}{{/crossLink}} that loads HTML tags for video and audio.
	 * @class MediaTagRequest
	 * @param {LoadItem} loadItem
	 * @param {HTMLAudioElement|HTMLVideoElement} tag
	 * @param {String} srcAttribute The tag attribute that specifies the source, such as "src", "href", etc.
	 * @constructor
	 */
	function MediaTagRequest(loadItem, tag, srcAttribute) {
		this.AbstractRequest_constructor(loadItem);

		// protected properties
		this._tag = tag;
		this._tagSrcAttribute = srcAttribute;
		this._loadedHandler = createjs.proxy(this._handleTagComplete, this);
	};

	var p = createjs.extend(MediaTagRequest, createjs.TagRequest);
	var s = MediaTagRequest;

	// public methods
	p.load = function () {
		var sc = createjs.proxy(this._handleStalled, this);
		this._stalledCallback = sc;

		var pc = createjs.proxy(this._handleProgress, this);
		this._handleProgress = pc;

		this._tag.addEventListener("stalled", sc);
		this._tag.addEventListener("progress", pc);

		// This will tell us when audio is buffered enough to play through, but not when its loaded.
		// The tag doesn't keep loading in Chrome once enough has buffered, and we have decided that behaviour is sufficient.
		this._tag.addEventListener && this._tag.addEventListener("canplaythrough", this._loadedHandler, false); // canplaythrough callback doesn't work in Chrome, so we use an event.

		this.TagRequest_load();
	};

	// private methods
	p._handleReadyStateChange = function () {
		clearTimeout(this._loadTimeout);
		// This is strictly for tags in browsers that do not support onload.
		var tag = this._tag;

		// Complete is for old IE support.
		if (tag.readyState == "loaded" || tag.readyState == "complete") {
			this._handleTagComplete();
		}
	};

	p._handleStalled = function () {
		//Ignore, let the timeout take care of it. Sometimes its not really stopped.
	};

	/**
	 * An XHR request has reported progress.
	 * @method _handleProgress
	 * @param {Object} event The XHR progress event.
	 * @private
	 */
	p._handleProgress = function (event) {
		if (!event || event.loaded > 0 && event.total == 0) {
			return; // Sometimes we get no "total", so just ignore the progress event.
		}

		var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
		this.dispatchEvent(newEvent);
	};

	// protected methods
	p._clean = function () {
		this._tag.removeEventListener && this._tag.removeEventListener("canplaythrough", this._loadedHandler);
		this._tag.removeEventListener("stalled", this._stalledCallback);
		this._tag.removeEventListener("progress", this._progressCallback);

		this.TagRequest__clean();
	};

	createjs.MediaTagRequest = createjs.promote(MediaTagRequest, "TagRequest");

}());

//##############################################################################
// XHRRequest.js
//##############################################################################

(function () {
	"use strict";

// constructor
	/**
	 * A preloader that loads items using XHR requests, usually XMLHttpRequest. However XDomainRequests will be used
	 * for cross-domain requests if possible, and older versions of IE fall back on to ActiveX objects when necessary.
	 * XHR requests load the content as text or binary data, provide progress and consistent completion events, and
	 * can be canceled during load. Note that XHR is not supported in IE 6 or earlier, and is not recommended for
	 * cross-domain loading.
	 * @class XHRRequest
	 * @constructor
	 * @param {Object} item The object that defines the file to load. Please see the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
	 * for an overview of supported file properties.
	 * @extends AbstractLoader
	 */
	function XHRRequest (item) {
		this.AbstractRequest_constructor(item);

		// protected properties
		/**
		 * A reference to the XHR request used to load the content.
		 * @property _request
		 * @type {XMLHttpRequest | XDomainRequest | ActiveX.XMLHTTP}
		 * @private
		 */
		this._request = null;

		/**
		 * A manual load timeout that is used for browsers that do not support the onTimeout event on XHR (XHR level 1,
		 * typically IE9).
		 * @property _loadTimeout
		 * @type {Number}
		 * @private
		 */
		this._loadTimeout = null;

		/**
		 * The browser's XHR (XMLHTTPRequest) version. Supported versions are 1 and 2. There is no official way to detect
		 * the version, so we use capabilities to make a best guess.
		 * @property _xhrLevel
		 * @type {Number}
		 * @default 1
		 * @private
		 */
		this._xhrLevel = 1;

		/**
		 * The response of a loaded file. This is set because it is expensive to look up constantly. This property will be
		 * null until the file is loaded.
		 * @property _response
		 * @type {mixed}
		 * @private
		 */
		this._response = null;

		/**
		 * The response of the loaded file before it is modified. In most cases, content is converted from raw text to
		 * an HTML tag or a formatted object which is set to the <code>result</code> property, but the developer may still
		 * want to access the raw content as it was loaded.
		 * @property _rawResponse
		 * @type {String|Object}
		 * @private
		 */
		this._rawResponse = null;

		this._canceled = false;

		// Setup our event handlers now.
		this._handleLoadStartProxy = createjs.proxy(this._handleLoadStart, this);
		this._handleProgressProxy = createjs.proxy(this._handleProgress, this);
		this._handleAbortProxy = createjs.proxy(this._handleAbort, this);
		this._handleErrorProxy = createjs.proxy(this._handleError, this);
		this._handleTimeoutProxy = createjs.proxy(this._handleTimeout, this);
		this._handleLoadProxy = createjs.proxy(this._handleLoad, this);
		this._handleReadyStateChangeProxy = createjs.proxy(this._handleReadyStateChange, this);

		if (!this._createXHR(item)) {
			//TODO: Throw error?
		}
	};

	var p = createjs.extend(XHRRequest, createjs.AbstractRequest);

// static properties
	/**
	 * A list of XMLHTTP object IDs to try when building an ActiveX object for XHR requests in earlier versions of IE.
	 * @property ACTIVEX_VERSIONS
	 * @type {Array}
	 * @since 0.4.2
	 * @private
	 */
	XHRRequest.ACTIVEX_VERSIONS = [
		"Msxml2.XMLHTTP.6.0",
		"Msxml2.XMLHTTP.5.0",
		"Msxml2.XMLHTTP.4.0",
		"MSXML2.XMLHTTP.3.0",
		"MSXML2.XMLHTTP",
		"Microsoft.XMLHTTP"
	];

// Public methods
	/**
	 * Look up the loaded result.
	 * @method getResult
	 * @param {Boolean} [raw=false] Return a raw result instead of a formatted result. This applies to content
	 * loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
	 * returned instead.
	 * @return {Object} A result object containing the content that was loaded, such as:
	 * <ul>
	 *      <li>An image tag (&lt;image /&gt;) for images</li>
	 *      <li>A script tag for JavaScript (&lt;script /&gt;). Note that scripts loaded with tags may be added to the
	 *      HTML head.</li>
	 *      <li>A style tag for CSS (&lt;style /&gt;)</li>
	 *      <li>Raw text for TEXT</li>
	 *      <li>A formatted JavaScript object defined by JSON</li>
	 *      <li>An XML document</li>
	 *      <li>An binary arraybuffer loaded by XHR</li>
	 * </ul>
	 * Note that if a raw result is requested, but not found, the result will be returned instead.
	 */
	p.getResult = function (raw) {
		if (raw && this._rawResponse) {
			return this._rawResponse;
		}
		return this._response;
	};

	// Overrides abstract method in AbstractRequest
	p.cancel = function () {
		this.canceled = true;
		this._clean();
		this._request.abort();
	};

	// Overrides abstract method in AbstractLoader
	p.load = function () {
		if (this._request == null) {
			this._handleError();
			return;
		}

		//Events
		if (this._request.addEventListener != null) {
			this._request.addEventListener("loadstart", this._handleLoadStartProxy, false);
			this._request.addEventListener("progress", this._handleProgressProxy, false);
			this._request.addEventListener("abort", this._handleAbortProxy, false);
			this._request.addEventListener("error", this._handleErrorProxy, false);
			this._request.addEventListener("timeout", this._handleTimeoutProxy, false);

			// Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
			this._request.addEventListener("load", this._handleLoadProxy, false);
			this._request.addEventListener("readystatechange", this._handleReadyStateChangeProxy, false);
		} else {
			// IE9 support
			this._request.onloadstart = this._handleLoadStartProxy;
			this._request.onprogress = this._handleProgressProxy;
			this._request.onabort = this._handleAbortProxy;
			this._request.onerror = this._handleErrorProxy;
			this._request.ontimeout = this._handleTimeoutProxy;

			// Note: We don't get onload in all browsers (earlier FF and IE). onReadyStateChange handles these.
			this._request.onload = this._handleLoadProxy;
			this._request.onreadystatechange = this._handleReadyStateChangeProxy;
		}

		// Set up a timeout if we don't have XHR2
		if (this._xhrLevel == 1) {
			this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);
		}

		// Sometimes we get back 404s immediately, particularly when there is a cross origin request.  // note this does not catch in Chrome
		try {
			if (!this._item.values || this._item.method == createjs.AbstractLoader.GET) {
				this._request.send();
			} else if (this._item.method == createjs.AbstractLoader.POST) {
				this._request.send(createjs.RequestUtils.formatQueryString(this._item.values));
			}
		} catch (error) {
			this.dispatchEvent(new createjs.ErrorEvent("XHR_SEND", null, error));
		}
	};

	p.setResponseType = function (type) {
		// Some old browsers doesn't support blob, so we convert arraybuffer to blob after response is downloaded
		if (type === 'blob') {
			type = window.URL ? 'blob' : 'arraybuffer';
			this._responseType = type;
		}
		this._request.responseType = type;
	};

	/**
	 * Get all the response headers from the XmlHttpRequest.
	 *
	 * <strong>From the docs:</strong> Return all the HTTP headers, excluding headers that are a case-insensitive match
	 * for Set-Cookie or Set-Cookie2, as a single string, with each header line separated by a U+000D CR U+000A LF pair,
	 * excluding the status line, and with each header name and header value separated by a U+003A COLON U+0020 SPACE
	 * pair.
	 * @method getAllResponseHeaders
	 * @return {String}
	 * @since 0.4.1
	 */
	p.getAllResponseHeaders = function () {
		if (this._request.getAllResponseHeaders instanceof Function) {
			return this._request.getAllResponseHeaders();
		} else {
			return null;
		}
	};

	/**
	 * Get a specific response header from the XmlHttpRequest.
	 *
	 * <strong>From the docs:</strong> Returns the header field value from the response of which the field name matches
	 * header, unless the field name is Set-Cookie or Set-Cookie2.
	 * @method getResponseHeader
	 * @param {String} header The header name to retrieve.
	 * @return {String}
	 * @since 0.4.1
	 */
	p.getResponseHeader = function (header) {
		if (this._request.getResponseHeader instanceof Function) {
			return this._request.getResponseHeader(header);
		} else {
			return null;
		}
	};

// protected methods
	/**
	 * The XHR request has reported progress.
	 * @method _handleProgress
	 * @param {Object} event The XHR progress event.
	 * @private
	 */
	p._handleProgress = function (event) {
		if (!event || event.loaded > 0 && event.total == 0) {
			return; // Sometimes we get no "total", so just ignore the progress event.
		}

		var newEvent = new createjs.ProgressEvent(event.loaded, event.total);
		this.dispatchEvent(newEvent);
	};

	/**
	 * The XHR request has reported a load start.
	 * @method _handleLoadStart
	 * @param {Object} event The XHR loadStart event.
	 * @private
	 */
	p._handleLoadStart = function (event) {
		clearTimeout(this._loadTimeout);
		this.dispatchEvent("loadstart");
	};

	/**
	 * The XHR request has reported an abort event.
	 * @method handleAbort
	 * @param {Object} event The XHR abort event.
	 * @private
	 */
	p._handleAbort = function (event) {
		this._clean();
		this.dispatchEvent(new createjs.ErrorEvent("XHR_ABORTED", null, event));
	};

	/**
	 * The XHR request has reported an error event.
	 * @method _handleError
	 * @param {Object} event The XHR error event.
	 * @private
	 */
	p._handleError = function (event) {
		this._clean();
		this.dispatchEvent(new createjs.ErrorEvent(event.message));
	};

	/**
	 * The XHR request has reported a readyState change. Note that older browsers (IE 7 & 8) do not provide an onload
	 * event, so we must monitor the readyStateChange to determine if the file is loaded.
	 * @method _handleReadyStateChange
	 * @param {Object} event The XHR readyStateChange event.
	 * @private
	 */
	p._handleReadyStateChange = function (event) {
		if (this._request.readyState == 4) {
			this._handleLoad();
		}
	};

	/**
	 * The XHR request has completed. This is called by the XHR request directly, or by a readyStateChange that has
	 * <code>request.readyState == 4</code>. Only the first call to this method will be processed.
	 * @method _handleLoad
	 * @param {Object} event The XHR load event.
	 * @private
	 */
	p._handleLoad = function (event) {
		if (this.loaded) {
			return;
		}
		this.loaded = true;

		var error = this._checkError();
		if (error) {
			this._handleError(error);
			return;
		}

		this._response = this._getResponse();
		// Convert arraybuffer back to blob
		if (this._responseType === 'arraybuffer') {
			try {
				this._response = new Blob([this._response]);
			} catch (e) {
				// Fallback to use BlobBuilder if Blob constructor is not supported
				// Tested on Android 2.3 ~ 4.2 and iOS5 safari
				window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
				if (e.name === 'TypeError' && window.BlobBuilder) {
					var builder = new BlobBuilder();
					builder.append(this._response);
					this._response = builder.getBlob();
				}
			}
		}
		this._clean();

		this.dispatchEvent(new createjs.Event("complete"));
	};

	/**
	 * The XHR request has timed out. This is called by the XHR request directly, or via a <code>setTimeout</code>
	 * callback.
	 * @method _handleTimeout
	 * @param {Object} [event] The XHR timeout event. This is occasionally null when called by the backup setTimeout.
	 * @private
	 */
	p._handleTimeout = function (event) {
		this._clean();

		this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
	};

// Protected
	/**
	 * Determine if there is an error in the current load. This checks the status of the request for problem codes. Note
	 * that this does not check for an actual response. Currently, it only checks for 404 or 0 error code.
	 * @method _checkError
	 * @return {int} If the request status returns an error code.
	 * @private
	 */
	p._checkError = function () {
		//LM: Probably need additional handlers here, maybe 501
		var status = parseInt(this._request.status);

		switch (status) {
			case 404:   // Not Found
			case 0:     // Not Loaded
				return new Error(status);
		}
		return null;
	};

	/**
	 * Validate the response. Different browsers have different approaches, some of which throw errors when accessed
	 * in other browsers. If there is no response, the <code>_response</code> property will remain null.
	 * @method _getResponse
	 * @private
	 */
	p._getResponse = function () {
		if (this._response != null) {
			return this._response;
		}

		if (this._request.response != null) {
			return this._request.response;
		}

		// Android 2.2 uses .responseText
		try {
			if (this._request.responseText != null) {
				return this._request.responseText;
			}
		} catch (e) {
		}

		// When loading XML, IE9 does not return .response, instead it returns responseXML.xml
		try {
			if (this._request.responseXML != null) {
				return this._request.responseXML;
			}
		} catch (e) {
		}

		return null;
	};

	/**
	 * Create an XHR request. Depending on a number of factors, we get totally different results.
	 * <ol><li>Some browsers get an <code>XDomainRequest</code> when loading cross-domain.</li>
	 *      <li>XMLHttpRequest are created when available.</li>
	 *      <li>ActiveX.XMLHTTP objects are used in older IE browsers.</li>
	 *      <li>Text requests override the mime type if possible</li>
	 *      <li>Origin headers are sent for crossdomain requests in some browsers.</li>
	 *      <li>Binary loads set the response type to "arraybuffer"</li></ol>
	 * @method _createXHR
	 * @param {Object} item The requested item that is being loaded.
	 * @return {Boolean} If an XHR request or equivalent was successfully created.
	 * @private
	 */
	p._createXHR = function (item) {
		// Check for cross-domain loads. We can't fully support them, but we can try.
		var crossdomain = createjs.RequestUtils.isCrossDomain(item);
		var headers = {};

		// Create the request. Fallback to whatever support we have.
		var req = null;
		if (window.XMLHttpRequest) {
			req = new XMLHttpRequest();
			// This is 8 or 9, so use XDomainRequest instead.
			if (crossdomain && req.withCredentials === undefined && window.XDomainRequest) {
				req = new XDomainRequest();
			}
		} else { // Old IE versions use a different approach
			for (var i = 0, l = s.ACTIVEX_VERSIONS.length; i < l; i++) {
				var axVersion = s.ACTIVEX_VERSIONS[i];
				try {
					req = new ActiveXObject(axVersion);
					break;
				} catch (e) {
				}
			}
			if (req == null) {
				return false;
			}
		}

		// Default to utf-8 for Text requests.
		if (item.mimeType == null && createjs.RequestUtils.isText(item.type)) {
			item.mimeType = "text/plain; charset=utf-8";
		}

		// IE9 doesn't support overrideMimeType(), so we need to check for it.
		if (item.mimeType && req.overrideMimeType) {
			req.overrideMimeType(item.mimeType);
		}

		// Determine the XHR level
		this._xhrLevel = (typeof req.responseType === "string") ? 2 : 1;

		var src = null;
		if (item.method == createjs.AbstractLoader.GET) {
			src = createjs.RequestUtils.buildPath(item.src, item.values);
		} else {
			src = item.src;
		}

		// Open the request.  Set cross-domain flags if it is supported (XHR level 1 only)
		req.open(item.method || createjs.AbstractLoader.GET, src, true);

		if (crossdomain && req instanceof XMLHttpRequest && this._xhrLevel == 1) {
			headers["Origin"] = location.origin;
		}

		// To send data we need to set the Content-type header)
		if (item.values && item.method == createjs.AbstractLoader.POST) {
			headers["Content-Type"] = "application/x-www-form-urlencoded";
		}

		if (!crossdomain && !headers["X-Requested-With"]) {
			headers["X-Requested-With"] = "XMLHttpRequest";
		}

		if (item.headers) {
			for (var n in item.headers) {
				headers[n] = item.headers[n];
			}
		}

		for (n in headers) {
			req.setRequestHeader(n, headers[n])
		}

		if (req instanceof XMLHttpRequest && item.withCredentials !== undefined) {
			req.withCredentials = item.withCredentials;
		}

		this._request = req;

		return true;
	};

	/**
	 * A request has completed (or failed or canceled), and needs to be disposed.
	 * @method _clean
	 * @private
	 */
	p._clean = function () {
		clearTimeout(this._loadTimeout);

		if (this._request.removeEventListener != null) {
			this._request.removeEventListener("loadstart", this._handleLoadStartProxy);
			this._request.removeEventListener("progress", this._handleProgressProxy);
			this._request.removeEventListener("abort", this._handleAbortProxy);
			this._request.removeEventListener("error", this._handleErrorProxy);
			this._request.removeEventListener("timeout", this._handleTimeoutProxy);
			this._request.removeEventListener("load", this._handleLoadProxy);
			this._request.removeEventListener("readystatechange", this._handleReadyStateChangeProxy);
		} else {
			this._request.onloadstart = null;
			this._request.onprogress = null;
			this._request.onabort = null;
			this._request.onerror = null;
			this._request.ontimeout = null;
			this._request.onload = null;
			this._request.onreadystatechange = null;
		}
	};

	p.toString = function () {
		return "[PreloadJS XHRRequest]";
	};

	createjs.XHRRequest = createjs.promote(XHRRequest, "AbstractRequest");

}());

//##############################################################################
// LoadQueue.js
//##############################################################################

/*
 TODO: WINDOWS ISSUES
 * No error for HTML audio in IE 678
 * SVG no failure error in IE 67 (maybe 8) TAGS AND XHR
 * No script complete handler in IE 67 TAGS (XHR is fine)
 * No XML/JSON in IE6 TAGS
 * Need to hide loading SVG in Opera TAGS
 * No CSS onload/readystatechange in Safari or Android TAGS (requires rule checking)
 * SVG no load or failure in Opera XHR
 * Reported issues with IE7/8
 */

(function () {
	"use strict";

// constructor
	/**
	 * The LoadQueue class is the main API for preloading content. LoadQueue is a load manager, which can preload either
	 * a single file, or queue of files.
	 *
	 * <b>Creating a Queue</b><br />
	 * To use LoadQueue, create a LoadQueue instance. If you want to force tag loading where possible, set the preferXHR
	 * argument to false.
	 *
	 *      var queue = new createjs.LoadQueue(true);
	 *
	 * <b>Listening for Events</b><br />
	 * Add any listeners you want to the queue. Since PreloadJS 0.3.0, the {{#crossLink "EventDispatcher"}}{{/crossLink}}
	 * lets you add as many listeners as you want for events. You can subscribe to the following events:<ul>
	 *     <li>{{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}}: fired when a queue completes loading all
	 *     files</li>
	 *     <li>{{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}: fired when the queue encounters an error with
	 *     any file.</li>
	 *     <li>{{#crossLink "AbstractLoader/progress:event"}}{{/crossLink}}: Progress for the entire queue has
	 *     changed.</li>
	 *     <li>{{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}}: A single file has completed loading.</li>
	 *     <li>{{#crossLink "LoadQueue/fileprogress:event"}}{{/crossLink}}: Progress for a single file has changes. Note
	 *     that only files loaded with XHR (or possibly by plugins) will fire progress events other than 0 or 100%.</li>
	 * </ul>
	 *
	 *      queue.on("fileload", handleFileLoad, this);
	 *      queue.on("complete", handleComplete, this);
	 *
	 * <b>Adding files and manifests</b><br />
	 * Add files you want to load using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or add multiple files at a
	 * time using a list or a manifest definition using {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. Files are
	 * appended to the end of the active queue, so you can use these methods as many times as you like, whenever you
	 * like.
	 *
	 *      queue.loadFile("filePath/file.jpg");
	 *      queue.loadFile({id:"image", src:"filePath/file.jpg"});
	 *      queue.loadManifest(["filePath/file.jpg", {id:"image", src:"filePath/file.jpg"}]);
	 *
	 *      // Use an external manifest
	 *      queue.loadManifest("path/to/manifest.json");
	 *      queue.loadManifest({src:"manifest.json", type:"manifest"});
	 *
	 * If you pass `false` as the `loadNow` parameter, the queue will not kick of the load of the files, but it will not
	 * stop if it has already been started. Call the {{#crossLink "AbstractLoader/load"}}{{/crossLink}} method to begin
	 * a paused queue. Note that a paused queue will automatically resume when new files are added to it with a
	 * `loadNow` argument of `true`.
	 *
	 *      queue.load();
	 *
	 * <b>File Types</b><br />
	 * The file type of a manifest item is auto-determined by the file extension. The pattern matching in PreloadJS
	 * should handle the majority of standard file and url formats, and works with common file extensions. If you have
	 * either a non-standard file extension, or are serving the file using a proxy script, then you can pass in a
	 * <code>type</code> property with any manifest item.
	 *
	 *      queue.loadFile({src:"path/to/myFile.mp3x", type:createjs.AbstractLoader.SOUND});
	 *
	 *      // Note that PreloadJS will not read a file extension from the query string
	 *      queue.loadFile({src:"http://server.com/proxy?file=image.jpg", type:createjs.AbstractLoader.IMAGE});
	 *
	 * Supported types are defined on the {{#crossLink "AbstractLoader"}}{{/crossLink}} class, and include:
	 * <ul>
	 *     <li>{{#crossLink "AbstractLoader/BINARY:property"}}{{/crossLink}}: Raw binary data via XHR</li>
	 *     <li>{{#crossLink "AbstractLoader/CSS:property"}}{{/crossLink}}: CSS files</li>
	 *     <li>{{#crossLink "AbstractLoader/IMAGE:property"}}{{/crossLink}}: Common image formats</li>
	 *     <li>{{#crossLink "AbstractLoader/JAVASCRIPT:property"}}{{/crossLink}}: JavaScript files</li>
	 *     <li>{{#crossLink "AbstractLoader/JSON:property"}}{{/crossLink}}: JSON data</li>
	 *     <li>{{#crossLink "AbstractLoader/JSONP:property"}}{{/crossLink}}: JSON files cross-domain</li>
	 *     <li>{{#crossLink "AbstractLoader/MANIFEST:property"}}{{/crossLink}}: A list of files to load in JSON format, see
	 *     {{#crossLink "AbstractLoader/loadManifest"}}{{/crossLink}}</li>
	 *     <li>{{#crossLink "AbstractLoader/SOUND:property"}}{{/crossLink}}: Audio file formats</li>
	 *     <li>{{#crossLink "AbstractLoader/SPRITESHEET:property"}}{{/crossLink}}: JSON SpriteSheet definitions. This
	 *     will also load sub-images, and provide a {{#crossLink "SpriteSheet"}}{{/crossLink}} instance.</li>
	 *     <li>{{#crossLink "AbstractLoader/SVG:property"}}{{/crossLink}}: SVG files</li>
	 *     <li>{{#crossLink "AbstractLoader/TEXT:property"}}{{/crossLink}}: Text files - XHR only</li>
     *     <li>{{#crossLink "AbstractLoader/VIDEO:property"}}{{/crossLink}}: Video objects</li>
	 *     <li>{{#crossLink "AbstractLoader/XML:property"}}{{/crossLink}}: XML data</li>
	 * </ul>
	 *
	 * <em>Note: Loader types used to be defined on LoadQueue, but have been moved to AbstractLoader for better
	 * portability of loader classes, which can be used individually now. The properties on LoadQueue still exist, but
	 * are deprecated.</em>
	 *
	 * <b>Handling Results</b><br />
	 * When a file is finished downloading, a {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event is
	 * dispatched. In an example above, there is an event listener snippet for fileload. Loaded files are usually a
	 * formatted object that can be used immediately, including:
	 * <ul>
	 *     <li>Binary: The binary loaded result</li>
	 *     <li>CSS: A &lt;link /&gt; tag</li>
	 *     <li>Image: An &lt;img /&gt; tag</li>
	 *     <li>JavaScript: A &lt;script /&gt; tag</li>
	 *     <li>JSON/JSONP: A formatted JavaScript Object</li>
	 *     <li>Manifest: A JavaScript object.
	 *     <li>Sound: An &lt;audio /&gt; tag</a>
	 *     <li>SpriteSheet: A {{#crossLink "SpriteSheet"}}{{/crossLink}} instance, containing loaded images.
	 *     <li>SVG: An &lt;object /&gt; tag</li>
	 *     <li>Text: Raw text</li>
     *     <li>Video: A Video DOM node</li>
	 *     <li>XML: An XML DOM node</li>
	 * </ul>
	 *
	 *      function handleFileLoad(event) {
	 *          var item = event.item; // A reference to the item that was passed in to the LoadQueue
	 *          var type = item.type;
	 *
	 *          // Add any images to the page body.
	 *          if (type == createjs.LoadQueue.IMAGE) {
	 *              document.body.appendChild(event.result);
	 *          }
	 *      }
	 *
	 * At any time after the file has been loaded (usually after the queue has completed), any result can be looked up
	 * via its "id" using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}. If no id was provided, then the
	 * "src" or file path can be used instead, including the `path` defined by a manifest, but <strong>not including</strong>
	 * a base path defined on the LoadQueue. It is recommended to always pass an id if you want to look up content.
	 *
	 *      var image = queue.getResult("image");
	 *      document.body.appendChild(image);
	 *
	 * Raw loaded content can be accessed using the <code>rawResult</code> property of the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}}
	 * event, or can be looked up using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}, passing `true` as the 2nd
	 * argument. This is only applicable for content that has been parsed for the browser, specifically: JavaScript,
	 * CSS, XML, SVG, and JSON objects, or anything loaded with XHR.
	 *
	 *      var image = queue.getResult("image", true); // load the binary image data loaded with XHR.
	 *
	 * <b>Plugins</b><br />
	 * LoadQueue has a simple plugin architecture to help process and preload content. For example, to preload audio,
	 * make sure to install the <a href="http://soundjs.com">SoundJS</a> Sound class, which will help load HTML audio,
	 * Flash audio, and WebAudio files. This should be installed <strong>before</strong> loading any audio files.
	 *
	 *      queue.installPlugin(createjs.Sound);
	 *
	 * <h4>Known Browser Issues</h4>
	 * <ul>
	 *     <li>Browsers without audio support can not load audio files.</li>
	 *     <li>Safari on Mac OS X can only play HTML audio if QuickTime is installed</li>
	 *     <li>HTML Audio tags will only download until their <code>canPlayThrough</code> event is fired. Browsers other
	 *     than Chrome will continue to download in the background.</li>
	 *     <li>When loading scripts using tags, they are automatically added to the document.</li>
	 *     <li>Scripts loaded via XHR may not be properly inspectable with browser tools.</li>
	 *     <li>IE6 and IE7 (and some other browsers) may not be able to load XML, Text, or JSON, since they require
	 *     XHR to work.</li>
	 *     <li>Content loaded via tags will not show progress, and will continue to download in the background when
	 *     canceled, although no events will be dispatched.</li>
	 * </ul>
	 *
	 * @class LoadQueue
	 * @param {Boolean} [preferXHR=true] Determines whether the preload instance will favor loading with XHR (XML HTTP
	 * Requests), or HTML tags. When this is `false`, the queue will use tag loading when possible, and fall back on XHR
	 * when necessary.
	 * @param {String} [basePath=""] A path that will be prepended on to the source parameter of all items in the queue
	 * before they are loaded.  Sources beginning with a protocol such as `http://` or a relative path such as `../`
	 * will not receive a base path.
	 * @param {String|Boolean} [crossOrigin=""] An optional flag to support images loaded from a CORS-enabled server. To
	 * use it, set this value to `true`, which will default the crossOrigin property on images to "Anonymous". Any
	 * string value will be passed through, but only "" and "Anonymous" are recommended. <strong>Note: The crossOrigin
	 * parameter is deprecated. Use LoadItem.crossOrigin instead</strong>
	 *
	 * @constructor
	 * @extends AbstractLoader
	 */
	function LoadQueue (preferXHR, basePath, crossOrigin) {
		this.AbstractLoader_constructor();

		/**
		 * An array of the plugins registered using {{#crossLink "LoadQueue/installPlugin"}}{{/crossLink}}.
		 * @property _plugins
		 * @type {Array}
		 * @private
		 * @since 0.6.1
		 */
		this._plugins = [];

		/**
		 * An object hash of callbacks that are fired for each file type before the file is loaded, giving plugins the
		 * ability to override properties of the load. Please see the {{#crossLink "LoadQueue/installPlugin"}}{{/crossLink}}
		 * method for more information.
		 * @property _typeCallbacks
		 * @type {Object}
		 * @private
		 */
		this._typeCallbacks = {};

		/**
		 * An object hash of callbacks that are fired for each file extension before the file is loaded, giving plugins the
		 * ability to override properties of the load. Please see the {{#crossLink "LoadQueue/installPlugin"}}{{/crossLink}}
		 * method for more information.
		 * @property _extensionCallbacks
		 * @type {null}
		 * @private
		 */
		this._extensionCallbacks = {};

		/**
		 * The next preload queue to process when this one is complete. If an error is thrown in the current queue, and
		 * {{#crossLink "LoadQueue/stopOnError:property"}}{{/crossLink}} is `true`, the next queue will not be processed.
		 * @property next
		 * @type {LoadQueue}
		 * @default null
		 */
		this.next = null;

		/**
		 * Ensure loaded scripts "complete" in the order they are specified. Loaded scripts are added to the document head
		 * once they are loaded. Scripts loaded via tags will load one-at-a-time when this property is `true`, whereas
		 * scripts loaded using XHR can load in any order, but will "finish" and be added to the document in the order
		 * specified.
		 *
		 * Any items can be set to load in order by setting the {{#crossLink "maintainOrder:property"}}{{/crossLink}}
		 * property on the load item, or by ensuring that only one connection can be open at a time using
		 * {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}. Note that when the `maintainScriptOrder` property
		 * is set to `true`, scripts items are automatically set to `maintainOrder=true`, and changing the
		 * `maintainScriptOrder` to `false` during a load will not change items already in a queue.
		 *
		 * <h4>Example</h4>
		 *
		 *      var queue = new createjs.LoadQueue();
		 *      queue.setMaxConnections(3); // Set a higher number to load multiple items at once
		 *      queue.maintainScriptOrder = true; // Ensure scripts are loaded in order
		 *      queue.loadManifest([
		 *          "script1.js",
		 *          "script2.js",
		 *          "image.png", // Load any time
		 *          {src: "image2.png", maintainOrder: true} // Will wait for script2.js
		 *          "image3.png",
		 *          "script3.js" // Will wait for image2.png before loading (or completing when loading with XHR)
		 *      ]);
		 *
		 * @property maintainScriptOrder
		 * @type {Boolean}
		 * @default true
		 */
		this.maintainScriptOrder = true;

		/**
		 * Determines if the LoadQueue will stop processing the current queue when an error is encountered.
		 * @property stopOnError
		 * @type {Boolean}
		 * @default false
		 */
		this.stopOnError = false;

		/**
		 * The number of maximum open connections that a loadQueue tries to maintain. Please see
		 * {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}} for more information.
		 * @property _maxConnections
		 * @type {Number}
		 * @default 1
		 * @private
		 */
		this._maxConnections = 1;

		/**
		 * An internal list of all the default Loaders that are included with PreloadJS. Before an item is loaded, the
		 * available loader list is iterated, in the order they are included, and as soon as a loader indicates it can
		 * handle the content, it will be selected. The default loader, ({{#crossLink "TextLoader"}}{{/crossLink}} is
		 * last in the list, so it will be used if no other match is found. Typically, loaders will match based on the
		 * {{#crossLink "LoadItem/type"}}{{/crossLink}}, which is automatically determined using the file extension of
		 * the {{#crossLink "LoadItem/src:property"}}{{/crossLink}}.
		 *
		 * Loaders can be removed from PreloadJS by simply not including them.
		 *
		 * Custom loaders installed using {{#crossLink "registerLoader"}}{{/crossLink}} will be prepended to this list
		 * so that they are checked first.
		 * @property _availableLoaders
		 * @type {Array}
		 * @private
		 * @since 0.6.0
		 */
		this._availableLoaders = [
			createjs.ImageLoader,
			createjs.JavaScriptLoader,
			createjs.CSSLoader,
			createjs.JSONLoader,
			createjs.JSONPLoader,
			createjs.SoundLoader,
			createjs.ManifestLoader,
			createjs.SpriteSheetLoader,
			createjs.XMLLoader,
			createjs.SVGLoader,
			createjs.BinaryLoader,
			createjs.VideoLoader,
			createjs.TextLoader
		];

		/**
		 * The number of built in loaders, so they can't be removed by {{#crossLink "unregisterLoader"}}{{/crossLink}.
				 * @property _defaultLoaderLength
		 * @type {Number}
		 * @private
		 * @since 0.6.0
		 */
		this._defaultLoaderLength = this._availableLoaders.length;

		this.init(preferXHR, basePath, crossOrigin);
	}

	var p = createjs.extend(LoadQueue, createjs.AbstractLoader);
	var s = LoadQueue;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.

	/**
	 * An internal initialization method, which is used for initial set up, but also to reset the LoadQueue.
	 * @method init
	 * @param preferXHR
	 * @param basePath
	 * @param crossOrigin
	 * @private
	 */
	p.init = function (preferXHR, basePath, crossOrigin) {

		// public properties
		/**
		 * @property useXHR
		 * @type {Boolean}
		 * @readonly
		 * @default true
		 * @deprecated Use preferXHR instead.
		 */
		this.useXHR = true;

		/**
		 * Try and use XMLHttpRequest (XHR) when possible. Note that LoadQueue will default to tag loading or XHR
		 * loading depending on the requirements for a media type. For example, HTML audio can not be loaded with XHR,
		 * and plain text can not be loaded with tags, so it will default the the correct type instead of using the
		 * user-defined type.
		 * @type {Boolean}
		 * @default true
		 * @since 0.6.0
		 */
		this.preferXHR = true; //TODO: Get/Set
		this._preferXHR = true;
		this.setPreferXHR(preferXHR);

		// protected properties
		/**
		 * Whether the queue is currently paused or not.
		 * @property _paused
		 * @type {boolean}
		 * @private
		 */
		this._paused = false;

		/**
		 * A path that will be prepended on to the item's {{#crossLink "LoadItem/src:property"}}{{/crossLink}}. The
		 * `_basePath` property will only be used if an item's source is relative, and does not include a protocol such
		 * as `http://`, or a relative path such as `../`.
		 * @property _basePath
		 * @type {String}
		 * @private
		 * @since 0.3.1
		 */
		this._basePath = basePath;

		/**
		 * An optional flag to set on images that are loaded using PreloadJS, which enables CORS support. Images loaded
		 * cross-domain by servers that support CORS require the crossOrigin flag to be loaded and interacted with by
		 * a canvas. When loading locally, or with a server with no CORS support, this flag can cause other security issues,
		 * so it is recommended to only set it if you are sure the server supports it. Currently, supported values are ""
		 * and "Anonymous".
		 * @property _crossOrigin
		 * @type {String}
		 * @default ""
		 * @private
		 * @since 0.4.1
		 */
		this._crossOrigin = crossOrigin;

		/**
		 * Determines if the loadStart event was dispatched already. This event is only fired one time, when the first
		 * file is requested.
		 * @property _loadStartWasDispatched
		 * @type {Boolean}
		 * @default false
		 * @private
		 */
		this._loadStartWasDispatched = false;

		/**
		 * Determines if there is currently a script loading. This helps ensure that only a single script loads at once when
		 * using a script tag to do preloading.
		 * @property _currentlyLoadingScript
		 * @type {Boolean}
		 * @private
		 */
		this._currentlyLoadingScript = null;

		/**
		 * An array containing the currently downloading files.
		 * @property _currentLoads
		 * @type {Array}
		 * @private
		 */
		this._currentLoads = [];

		/**
		 * An array containing the queued items that have not yet started downloading.
		 * @property _loadQueue
		 * @type {Array}
		 * @private
		 */
		this._loadQueue = [];

		/**
		 * An array containing downloads that have not completed, so that the LoadQueue can be properly reset.
		 * @property _loadQueueBackup
		 * @type {Array}
		 * @private
		 */
		this._loadQueueBackup = [];

		/**
		 * An object hash of items that have finished downloading, indexed by the {{#crossLink "LoadItem"}}{{/crossLink}}
		 * id.
		 * @property _loadItemsById
		 * @type {Object}
		 * @private
		 */
		this._loadItemsById = {};

		/**
		 * An object hash of items that have finished downloading, indexed by {{#crossLink "LoadItem"}}{{/crossLink}}
		 * source.
		 * @property _loadItemsBySrc
		 * @type {Object}
		 * @private
		 */
		this._loadItemsBySrc = {};

		/**
		 * An object hash of loaded items, indexed by the ID of the {{#crossLink "LoadItem"}}{{/crossLink}}.
		 * @property _loadedResults
		 * @type {Object}
		 * @private
		 */
		this._loadedResults = {};

		/**
		 * An object hash of un-parsed loaded items, indexed by the ID of the {{#crossLink "LoadItem"}}{{/crossLink}}.
		 * @property _loadedRawResults
		 * @type {Object}
		 * @private
		 */
		this._loadedRawResults = {};

		/**
		 * The number of items that have been requested. This helps manage an overall progress without knowing how large
		 * the files are before they are downloaded. This does not include items inside of loaders such as the
		 * {{#crossLink "ManifestLoader"}}{{/crossLink}}.
		 * @property _numItems
		 * @type {Number}
		 * @default 0
		 * @private
		 */
		this._numItems = 0;

		/**
		 * The number of items that have completed loaded. This helps manage an overall progress without knowing how large
		 * the files are before they are downloaded.
		 * @property _numItemsLoaded
		 * @type {Number}
		 * @default 0
		 * @private
		 */
		this._numItemsLoaded = 0;

		/**
		 * A list of scripts in the order they were requested. This helps ensure that scripts are "completed" in the right
		 * order.
		 * @property _scriptOrder
		 * @type {Array}
		 * @private
		 */
		this._scriptOrder = [];

		/**
		 * A list of scripts that have been loaded. Items are added to this list as <code>null</code> when they are
		 * requested, contain the loaded item if it has completed, but not been dispatched to the user, and <code>true</true>
		 * once they are complete and have been dispatched.
		 * @property _loadedScripts
		 * @type {Array}
		 * @private
		 */
		this._loadedScripts = [];

		/**
		 * The last progress amount. This is used to suppress duplicate progress events.
		 * @property _lastProgress
		 * @type {Number}
		 * @private
		 * @since 0.6.0
		 */
		this._lastProgress = NaN;

	};

// static properties
	/**
	 * The time in milliseconds to assume a load has failed. An {{#crossLink "AbstractLoader/error:event"}}{{/crossLink}}
	 * event is dispatched if the timeout is reached before any data is received.
	 * @property loadTimeout
	 * @type {Number}
	 * @default 8000
	 * @static
	 * @since 0.4.1
	 * @deprecated In favour of {{#crossLink "LoadItem/LOAD_TIMEOUT_DEFAULT:property}}{{/crossLink}} property.
	 */
	s.loadTimeout = 8000;

	/**
	 * The time in milliseconds to assume a load has failed.
	 * @property LOAD_TIMEOUT
	 * @type {Number}
	 * @default 0
	 * @deprecated in favor of the {{#crossLink "LoadQueue/loadTimeout:property"}}{{/crossLink}} property.
	 */
	s.LOAD_TIMEOUT = 0;

// Preload Types
	/**
	 * @property BINARY
	 * @type {String}
	 * @default binary
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/BINARY:property"}}{{/crossLink}} instead.
	 */
	s.BINARY = createjs.AbstractLoader.BINARY;

	/**
	 * @property CSS
	 * @type {String}
	 * @default css
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/CSS:property"}}{{/crossLink}} instead.
	 */
	s.CSS = createjs.AbstractLoader.CSS;

	/**
	 * @property IMAGE
	 * @type {String}
	 * @default image
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/CSS:property"}}{{/crossLink}} instead.
	 */
	s.IMAGE = createjs.AbstractLoader.IMAGE;

	/**
	 * @property JAVASCRIPT
	 * @type {String}
	 * @default javascript
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/JAVASCRIPT:property"}}{{/crossLink}} instead.
	 */
	s.JAVASCRIPT = createjs.AbstractLoader.JAVASCRIPT;

	/**
	 * @property JSON
	 * @type {String}
	 * @default json
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/JSON:property"}}{{/crossLink}} instead.
	 */
	s.JSON = createjs.AbstractLoader.JSON;

	/**
	 * @property JSONP
	 * @type {String}
	 * @default jsonp
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/JSONP:property"}}{{/crossLink}} instead.
	 */
	s.JSONP = createjs.AbstractLoader.JSONP;

	/**
	 * @property MANIFEST
	 * @type {String}
	 * @default manifest
	 * @static
	 * @since 0.4.1
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/MANIFEST:property"}}{{/crossLink}} instead.
	 */
	s.MANIFEST = createjs.AbstractLoader.MANIFEST;

	/**
	 * @property SOUND
	 * @type {String}
	 * @default sound
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/JAVASCRIPT:property"}}{{/crossLink}} instead.
	 */
	s.SOUND = createjs.AbstractLoader.SOUND;

	/**
	 * @property VIDEO
	 * @type {String}
	 * @default video
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/JAVASCRIPT:property"}}{{/crossLink}} instead.
	 */
	s.VIDEO = createjs.AbstractLoader.VIDEO;

	/**
	 * @property SVG
	 * @type {String}
	 * @default svg
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/SVG:property"}}{{/crossLink}} instead.
	 */
	s.SVG = createjs.AbstractLoader.SVG;

	/**
	 * @property TEXT
	 * @type {String}
	 * @default text
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/TEXT:property"}}{{/crossLink}} instead.
	 */
	s.TEXT = createjs.AbstractLoader.TEXT;

	/**
	 * @property XML
	 * @type {String}
	 * @default xml
	 * @static
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/XML:property"}}{{/crossLink}} instead.
	 */
	s.XML = createjs.AbstractLoader.XML;

	/**
	 * @property POST
	 * @type {string}
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/POST:property"}}{{/crossLink}} instead.
	 */
	s.POST = createjs.AbstractLoader.POST;

	/**
	 * @property GET
	 * @type {string}
	 * @deprecated Use the AbstractLoader {{#crossLink "AbstractLoader/GET:property"}}{{/crossLink}} instead.
	 */
	s.GET = createjs.AbstractLoader.GET;

// events
	/**
	 * This event is fired when an individual file has loaded, and been processed.
	 * @event fileload
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
	 * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
	 * object will contain that value as a `src` property.
	 * @param {Object} result The HTML tag or parsed result of the loaded item.
	 * @param {Object} rawResult The unprocessed result, usually the raw text or binary data before it is converted
	 * to a usable object.
	 * @since 0.3.0
	 */

	/**
	 * This {{#crossLink "ProgressEvent"}}{{/crossLink}} that is fired when an an individual file's progress changes.
	 * @event fileprogress
	 * @since 0.3.0
	 */

	/**
	 * This event is fired when an individual file starts to load.
	 * @event filestart
	 * @param {Object} The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {Object} item The file item which was specified in the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
	 * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}} call. If only a string path or tag was specified, the
	 * object will contain that value as a property.
	 */

	/**
	 * Although it extends {{#crossLink "AbstractLoader"}}{{/crossLink}}, the `initialize` event is never fired from
	 * a LoadQueue instance.
	 * @event initialize
	 * @private
	 */

// public methods
	/**
	 * Register a custom loaders class. New loaders are given precedence over loaders added earlier and default loaders.
	 * It is recommended that loaders extend {{#crossLink "AbstractLoader"}}{{/crossLink}}. Loaders can only be added
	 * once, and will be prepended to the list of available loaders.
	 * @method registerLoader
	 * @param {Function|AbstractLoader} loader The AbstractLoader class to add.
	 * @since 0.6.0
	 */
	p.registerLoader = function (loader) {
		if (!loader || !loader.canLoadItem) {
			throw new Error("loader is of an incorrect type.");
		} else if (this._availableLoaders.indexOf(loader) != -1) {
			throw new Error("loader already exists."); //LM: Maybe just silently fail here
		}

		this._availableLoaders.unshift(loader);
	};

	/**
	 * Remove a custom loader added using {{#crossLink "registerLoader"}}{{/crossLink}}. Only custom loaders can be
	 * unregistered, the default loaders will always be available.
	 * @method unregisterLoader
	 * @param {Function|AbstractLoader} loader The AbstractLoader class to remove
	 */
	p.unregisterLoader = function (loader) {
		var idx = this._availableLoaders.indexOf(loader);
		if (idx != -1 && idx < this._defaultLoaderLength - 1) {
			this._availableLoaders.splice(idx, 1);
		}
	};

	/**
	 * @method setUseXHR
	 * @param {Boolean} value The new useXHR value to set.
	 * @return {Boolean} The new useXHR value. If XHR is not supported by the browser, this will return false, even if
	 * the provided value argument was true.
	 * @since 0.3.0
	 * @deprecated use the {{#crossLink "LoadQueue/preferXHR:property"}}{{/crossLink}} property, or the
	 * {{#crossLink "LoadQueue/setUseXHR"}}{{/crossLink}} method instead.
	 */
	p.setUseXHR = function (value) {
		return this.setPreferXHR(value);
	};

	/**
	 * Change the {{#crossLink "preferXHR:property"}}{{/crossLink}} value. Note that if this is set to `true`, it may
	 * fail, or be ignored depending on the browser's capabilities and the load type.
	 * @method setPreferXHR
	 * @param {Boolean} value
	 * @returns {Boolean} The value of {{#crossLink "preferXHR"}}{{/crossLink}} that was successfully set.
	 * @since 0.6.0
	 */
	p.setPreferXHR = function (value) {
		// Determine if we can use XHR. XHR defaults to TRUE, but the browser may not support it.
		//TODO: Should we be checking for the other XHR types? Might have to do a try/catch on the different types similar to createXHR.
		this.preferXHR = (value != false && window.XMLHttpRequest != null);
		return this.preferXHR;
	};

	/**
	 * Stops all queued and loading items, and clears the queue. This also removes all internal references to loaded
	 * content, and allows the queue to be used again.
	 * @method removeAll
	 * @since 0.3.0
	 */
	p.removeAll = function () {
		this.remove();
	};

	/**
	 * Stops an item from being loaded, and removes it from the queue. If nothing is passed, all items are removed.
	 * This also removes internal references to loaded item(s).
	 *
	 * <h4>Example</h4>
	 *
	 *      queue.loadManifest([
	 *          {src:"test.png", id:"png"},
	 *          {src:"test.jpg", id:"jpg"},
	 *          {src:"test.mp3", id:"mp3"}
	 *      ]);
	 *      queue.remove("png"); // Single item by ID
	 *      queue.remove("png", "test.jpg"); // Items as arguments. Mixed id and src.
	 *      queue.remove(["test.png", "jpg"]); // Items in an Array. Mixed id and src.
	 *
	 * @method remove
	 * @param {String | Array} idsOrUrls* The id or ids to remove from this queue. You can pass an item, an array of
	 * items, or multiple items as arguments.
	 * @since 0.3.0
	 */
	p.remove = function (idsOrUrls) {
		var args = null;

		if (idsOrUrls && !Array.isArray(idsOrUrls)) {
			args = [idsOrUrls];
		} else if (idsOrUrls) {
			args = idsOrUrls;
		} else if (arguments.length > 0) {
			return;
		}

		var itemsWereRemoved = false;

		// Destroy everything
		if (!args) {
			this.close();
			for (var n in this._loadItemsById) {
				this._disposeItem(this._loadItemsById[n]);
			}
			this.init(this.preferXHR, this._basePath);

			// Remove specific items
		} else {
			while (args.length) {
				var item = args.pop();
				var r = this.getResult(item);

				//Remove from the main load Queue
				for (i = this._loadQueue.length - 1; i >= 0; i--) {
					loadItem = this._loadQueue[i].getItem();
					if (loadItem.id == item || loadItem.src == item) {
						this._loadQueue.splice(i, 1)[0].cancel();
						break;
					}
				}

				//Remove from the backup queue
				for (i = this._loadQueueBackup.length - 1; i >= 0; i--) {
					loadItem = this._loadQueueBackup[i].getItem();
					if (loadItem.id == item || loadItem.src == item) {
						this._loadQueueBackup.splice(i, 1)[0].cancel();
						break;
					}
				}

				if (r) {
					this._disposeItem(this.getItem(item));
				} else {
					for (var i = this._currentLoads.length - 1; i >= 0; i--) {
						var loadItem = this._currentLoads[i].getItem();
						if (loadItem.id == item || loadItem.src == item) {
							this._currentLoads.splice(i, 1)[0].cancel();
							itemsWereRemoved = true;
							break;
						}
					}
				}
			}

			// If this was called during a load, try to load the next item.
			if (itemsWereRemoved) {
				this._loadNext();
			}
		}
	};

	/**
	 * Stops all open loads, destroys any loaded items, and resets the queue, so all items can
	 * be reloaded again by calling {{#crossLink "AbstractLoader/load"}}{{/crossLink}}. Items are not removed from the
	 * queue. To remove items use the {{#crossLink "LoadQueue/remove"}}{{/crossLink}} or
	 * {{#crossLink "LoadQueue/removeAll"}}{{/crossLink}} method.
	 * @method reset
	 * @since 0.3.0
	 */
	p.reset = function () {
		this.close();
		for (var n in this._loadItemsById) {
			this._disposeItem(this._loadItemsById[n]);
		}

		//Reset the queue to its start state
		var a = [];
		for (var i = 0, l = this._loadQueueBackup.length; i < l; i++) {
			a.push(this._loadQueueBackup[i].getItem());
		}

		this.loadManifest(a, false);
	};

	/**
	 * Register a plugin. Plugins can map to load types (sound, image, etc), or specific extensions (png, mp3, etc).
	 * Currently, only one plugin can exist per type/extension.
	 *
	 * When a plugin is installed, a <code>getPreloadHandlers()</code> method will be called on it. For more information
	 * on this method, check out the {{#crossLink "SamplePlugin/getPreloadHandlers"}}{{/crossLink}} method in the
	 * {{#crossLink "SamplePlugin"}}{{/crossLink}} class.
	 *
	 * Before a file is loaded, a matching plugin has an opportunity to modify the load. If a `callback` is returned
	 * from the {{#crossLink "SamplePlugin/getPreloadHandlers"}}{{/crossLink}} method, it will be invoked first, and its
	 * result may cancel or modify the item. The callback method can also return a `completeHandler` to be fired when
	 * the file is loaded, or a `tag` object, which will manage the actual download. For more information on these
	 * methods, check out the {{#crossLink "SamplePlugin/preloadHandler"}}{{/crossLink}} and {{#crossLink "SamplePlugin/fileLoadHandler"}}{{/crossLink}}
	 * methods on the {{#crossLink "SamplePlugin"}}{{/crossLink}}.
	 *
	 * @method installPlugin
	 * @param {Function} plugin The plugin class to install.
	 */
	p.installPlugin = function (plugin) {
		if (plugin == null) {
			return;
		}

		if (plugin.getPreloadHandlers != null) {
			this._plugins.push(plugin);
			var map = plugin.getPreloadHandlers();
			map.scope = plugin;

			if (map.types != null) {
				for (var i = 0, l = map.types.length; i < l; i++) {
					this._typeCallbacks[map.types[i]] = map;
				}
			}

			if (map.extensions != null) {
				for (i = 0, l = map.extensions.length; i < l; i++) {
					this._extensionCallbacks[map.extensions[i]] = map;
				}
			}
		}
	};

	/**
	 * Set the maximum number of concurrent connections. Note that browsers and servers may have a built-in maximum
	 * number of open connections, so any additional connections may remain in a pending state until the browser
	 * opens the connection. When loading scripts using tags, and when {{#crossLink "LoadQueue/maintainScriptOrder:property"}}{{/crossLink}}
	 * is `true`, only one script is loaded at a time due to browser limitations.
	 *
	 * <h4>Example</h4>
	 *
	 *      var queue = new createjs.LoadQueue();
	 *      queue.setMaxConnections(10); // Allow 10 concurrent loads
	 *
	 * @method setMaxConnections
	 * @param {Number} value The number of concurrent loads to allow. By default, only a single connection per LoadQueue
	 * is open at any time.
	 */
	p.setMaxConnections = function (value) {
		this._maxConnections = value;
		if (!this._paused && this._loadQueue.length > 0) {
			this._loadNext();
		}
	};

	/**
	 * Load a single file. To add multiple files at once, use the {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
	 * method.
	 *
	 * Files are always appended to the current queue, so this method can be used multiple times to add files.
	 * To clear the queue first, use the {{#crossLink "AbstractLoader/close"}}{{/crossLink}} method.
	 * @method loadFile
	 * @param {LoadItem|Object|String} file The file object or path to load. A file can be either
	 * <ul>
	 *     <li>A {{#crossLink "LoadItem"}}{{/crossLink}} instance</li>
	 *     <li>An object containing properties defined by {{#crossLink "LoadItem"}}{{/crossLink}}</li>
	 *     <li>OR A string path to a resource. Note that this kind of load item will be converted to a {{#crossLink "LoadItem"}}{{/crossLink}}
	 *     in the background.</li>
	 * </ul>
	 * @param {Boolean} [loadNow=true] Kick off an immediate load (true) or wait for a load call (false). The default
	 * value is true. If the queue is paused using {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}}, and the value is
	 * `true`, the queue will resume automatically.
	 * @param {String} [basePath] A base path that will be prepended to each file. The basePath argument overrides the
	 * path specified in the constructor. Note that if you load a manifest using a file of type {{#crossLink "AbstractLoader/MANIFEST:property"}}{{/crossLink}},
	 * its files will <strong>NOT</strong> use the basePath parameter. <strong>The basePath parameter is deprecated.</strong>
	 * This parameter will be removed in a future version. Please either use the `basePath` parameter in the LoadQueue
	 * constructor, or a `path` property in a manifest definition.
	 */
	p.loadFile = function (file, loadNow, basePath) {
		if (file == null) {
			var event = new createjs.ErrorEvent("PRELOAD_NO_FILE");
			this._sendError(event);
			return;
		}
		this._addItem(file, null, basePath);

		if (loadNow !== false) {
			this.setPaused(false);
		} else {
			this.setPaused(true);
		}
	};

	/**
	 * Load an array of files. To load a single file, use the {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} method.
	 * The files in the manifest are requested in the same order, but may complete in a different order if the max
	 * connections are set above 1 using {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}}. Scripts will load
	 * in the right order as long as {{#crossLink "LoadQueue/maintainScriptOrder"}}{{/crossLink}} is true (which is
	 * default).
	 *
	 * Files are always appended to the current queue, so this method can be used multiple times to add files.
	 * To clear the queue first, use the {{#crossLink "AbstractLoader/close"}}{{/crossLink}} method.
	 * @method loadManifest
	 * @param {Array|String|Object} manifest An list of files to load. The loadManifest call supports four types of
	 * manifests:
	 * <ol>
	 *     <li>A string path, which points to a manifest file, which is a JSON file that contains a "manifest" property,
	 *     which defines the list of files to load, and can optionally contain a "path" property, which will be
	 *     prepended to each file in the list.</li>
	 *     <li>An object which defines a "src", which is a JSON or JSONP file. A "callback" can be defined for JSONP
	 *     file. The JSON/JSONP file should contain a "manifest" property, which defines the list of files to load,
	 *     and can optionally contain a "path" property, which will be prepended to each file in the list.</li>
	 *     <li>An object which contains a "manifest" property, which defines the list of files to load, and can
	 *     optionally contain a "path" property, which will be prepended to each file in the list.</li>
	 *     <li>An Array of files to load.</li>
	 * </ol>
	 *
	 * Each "file" in a manifest can be either:
	 * <ul>
	 *     <li>A {{#crossLink "LoadItem"}}{{/crossLink}} instance</li>
	 *     <li>An object containing properties defined by {{#crossLink "LoadItem"}}{{/crossLink}}</li>
	 *     <li>OR A string path to a resource. Note that this kind of load item will be converted to a {{#crossLink "LoadItem"}}{{/crossLink}}
	 *     in the background.</li>
	 * </ul>
	 *
	 * @param {Boolean} [loadNow=true] Kick off an immediate load (true) or wait for a load call (false). The default
	 * value is true. If the queue is paused using {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}} and this value is
	 * `true`, the queue will resume automatically.
	 * @param {String} [basePath] A base path that will be prepended to each file. The basePath argument overrides the
	 * path specified in the constructor. Note that if you load a manifest using a file of type {{#crossLink "LoadQueue/MANIFEST:property"}}{{/crossLink}},
	 * its files will <strong>NOT</strong> use the basePath parameter. <strong>The basePath parameter is deprecated.</strong>
	 * This parameter will be removed in a future version. Please either use the `basePath` parameter in the LoadQueue
	 * constructor, or a `path` property in a manifest definition.
	 */
	p.loadManifest = function (manifest, loadNow, basePath) {
		var fileList = null;
		var path = null;

		// Array-based list of items
		if (Array.isArray(manifest)) {
			if (manifest.length == 0) {
				var event = new createjs.ErrorEvent("PRELOAD_MANIFEST_EMPTY");
				this._sendError(event);
				return;
			}
			fileList = manifest;

			// String-based. Only file manifests can be specified this way. Any other types will cause an error when loaded.
		} else if (typeof(manifest) === "string") {
			fileList = [
				{
					src: manifest,
					type: s.MANIFEST
				}
			];

		} else if (typeof(manifest) == "object") {

			// An object that defines a manifest path
			if (manifest.src !== undefined) {
				if (manifest.type == null) {
					manifest.type = s.MANIFEST;
				} else if (manifest.type != s.MANIFEST) {
					var event = new createjs.ErrorEvent("PRELOAD_MANIFEST_TYPE");
					this._sendError(event);
				}
				fileList = [manifest];

				// An object that defines a manifest
			} else if (manifest.manifest !== undefined) {
				fileList = manifest.manifest;
				path = manifest.path;
			}

			// Unsupported. This will throw an error.
		} else {
			var event = new createjs.ErrorEvent("PRELOAD_MANIFEST_NULL");
			this._sendError(event);
			return;
		}

		for (var i = 0, l = fileList.length; i < l; i++) {
			this._addItem(fileList[i], path, basePath);
		}

		if (loadNow !== false) {
			this.setPaused(false);
		} else {
			this.setPaused(true);
		}

	};

	/**
	 * Start a LoadQueue that was created, but not automatically started.
	 * @method load
	 */
	p.load = function () {
		this.setPaused(false);
	};

	/**
	 * Look up a {{#crossLink "LoadItem"}}{{/crossLink}} using either the "id" or "src" that was specified when loading it. Note that if no "id" was
	 * supplied with the load item, the ID will be the "src", including a `path` property defined by a manifest. The
	 * `basePath` will not be part of the ID.
	 * @method getItem
	 * @param {String} value The <code>id</code> or <code>src</code> of the load item.
	 * @return {Object} The load item that was initially requested using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}}
	 * or {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}. This object is also returned via the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}}
	 * event as the `item` parameter.
	 */
	p.getItem = function (value) {
		return this._loadItemsById[value] || this._loadItemsBySrc[value];
	};

	/**
	 * Look up a loaded result using either the "id" or "src" that was specified when loading it. Note that if no "id"
	 * was supplied with the load item, the ID will be the "src", including a `path` property defined by a manifest. The
	 * `basePath` will not be part of the ID.
	 * @method getResult
	 * @param {String} value The <code>id</code> or <code>src</code> of the load item.
	 * @param {Boolean} [rawResult=false] Return a raw result instead of a formatted result. This applies to content
	 * loaded via XHR such as scripts, XML, CSS, and Images. If there is no raw result, the formatted result will be
	 * returned instead.
	 * @return {Object} A result object containing the content that was loaded, such as:
	 * <ul>
	 *      <li>An image tag (&lt;image /&gt;) for images</li>
	 *      <li>A script tag for JavaScript (&lt;script /&gt;). Note that scripts are automatically added to the HTML
	 *      DOM.</li>
	 *      <li>A style tag for CSS (&lt;style /&gt; or &lt;link &gt;)</li>
	 *      <li>Raw text for TEXT</li>
	 *      <li>A formatted JavaScript object defined by JSON</li>
	 *      <li>An XML document</li>
	 *      <li>A binary arraybuffer loaded by XHR</li>
	 *      <li>An audio tag (&lt;audio &gt;) for HTML audio. Note that it is recommended to use SoundJS APIs to play
	 *      loaded audio. Specifically, audio loaded by Flash and WebAudio will return a loader object using this method
	 *      which can not be used to play audio back.</li>
	 * </ul>
	 * This object is also returned via the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event as the 'item`
	 * parameter. Note that if a raw result is requested, but not found, the result will be returned instead.
	 */
	p.getResult = function (value, rawResult) {
		var item = this._loadItemsById[value] || this._loadItemsBySrc[value];
		if (item == null) {
			return null;
		}
		var id = item.id;
		if (rawResult && this._loadedRawResults[id]) {
			return this._loadedRawResults[id];
		}
		return this._loadedResults[id];
	};

	/**
	 * Generate an list of items loaded by this queue.
	 * @method getItems
	 * @param {Boolean} loaded Determines if only items that have been loaded should be returned. If false, in-progress
	 * and failed load items will also be included.
	 * @returns {Array} A list of objects that have been loaded. Each item includes the {{#crossLink "LoadItem"}}{{/crossLink}},
	 * result, and rawResult.
	 * @since 0.6.0
	 */
	p.getItems = function (loaded) {
		var arr = [];
		for (var n in this._loadItemsById) {
			var item = this._loadItemsById[n];
			var result = this.getResult(n);
			if (loaded === true && result == null) {
				continue;
			}
			arr.push({
				item: item,
				result: result,
				rawResult: this.getResult(n, true)
			});
		}
		return arr;
	};

	/**
	 * Pause or resume the current load. Active loads will not be cancelled, but the next items in the queue will not
	 * be processed when active loads complete. LoadQueues are not paused by default.
	 *
	 * Note that if new items are added to the queue using {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} or
	 * {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}, a paused queue will be resumed, unless the `loadNow`
	 * argument is `false`.
	 * @method setPaused
	 * @param {Boolean} value Whether the queue should be paused or not.
	 */
	p.setPaused = function (value) {
		this._paused = value;
		if (!this._paused) {
			this._loadNext();
		}
	};

	/**
	 * Close the active queue. Closing a queue completely empties the queue, and prevents any remaining items from
	 * starting to download. Note that currently any active loads will remain open, and events may be processed.
	 *
	 * To stop and restart a queue, use the {{#crossLink "LoadQueue/setPaused"}}{{/crossLink}} method instead.
	 * @method close
	 */
	p.close = function () {
		while (this._currentLoads.length) {
			this._currentLoads.pop().cancel();
		}
		this._scriptOrder.length = 0;
		this._loadedScripts.length = 0;
		this.loadStartWasDispatched = false;
		this._itemCount = 0;
		this._lastProgress = NaN;
	};

// protected methods
	/**
	 * Add an item to the queue. Items are formatted into a usable object containing all the properties necessary to
	 * load the content. The load queue is populated with the loader instance that handles preloading, and not the load
	 * item that was passed in by the user. To look up the load item by id or src, use the {{#crossLink "LoadQueue.getItem"}}{{/crossLink}}
	 * method.
	 * @method _addItem
	 * @param {String|Object} value The item to add to the queue.
	 * @param {String} [path] An optional path prepended to the `src`. The path will only be prepended if the src is
	 * relative, and does not start with a protocol such as `http://`, or a path like `../`. If the LoadQueue was
	 * provided a {{#crossLink "_basePath"}}{{/crossLink}}, then it will optionally be prepended after.
	 * @param {String} [basePath] <strong>Deprecated</strong>An optional basePath passed into a {{#crossLink "LoadQueue/loadManifest"}}{{/crossLink}}
	 * or {{#crossLink "LoadQueue/loadFile"}}{{/crossLink}} call. This parameter will be removed in a future tagged
	 * version.
	 * @private
	 */
	p._addItem = function (value, path, basePath) {
		var item = this._createLoadItem(value, path, basePath); // basePath and manifest path are added to the src.
		if (item == null) {
			return;
		} // Sometimes plugins or types should be skipped.
		var loader = this._createLoader(item);
		if (loader != null) {
			if ("plugins" in loader) {
				loader.plugins = this._plugins;
			}
			item._loader = loader;
			this._loadQueue.push(loader);
			this._loadQueueBackup.push(loader);

			this._numItems++;
			this._updateProgress();

			// Only worry about script order when using XHR to load scripts. Tags are only loading one at a time.
			if ((this.maintainScriptOrder
					&& item.type == createjs.LoadQueue.JAVASCRIPT
						//&& loader instanceof createjs.XHRLoader //NOTE: Have to track all JS files this way
					)
					|| item.maintainOrder === true) {
				this._scriptOrder.push(item);
				this._loadedScripts.push(null);
			}
		}
	};

	/**
	 * Create a refined {{#crossLink "LoadItem"}}{{/crossLink}}, which contains all the required properties. The type of
	 * item is determined by browser support, requirements based on the file type, and developer settings. For example,
	 * XHR is only used for file types that support it in new browsers.
	 *
	 * Before the item is returned, any plugins registered to handle the type or extension will be fired, which may
	 * alter the load item.
	 * @method _createLoadItem
	 * @param {String | Object | HTMLAudioElement | HTMLImageElement} value The item that needs to be preloaded.
	 * @param {String} [path] A path to prepend to the item's source. Sources beginning with http:// or similar will
	 * not receive a path. Since PreloadJS 0.4.1, the src will be modified to include the `path` and {{#crossLink "LoadQueue/_basePath:property"}}{{/crossLink}}
	 * when it is added.
	 * @param {String} [basePath] <strong>Deprectated</strong> A base path to prepend to the items source in addition to
	 * the path argument.
	 * @return {Object} The loader instance that will be used.
	 * @private
	 */
	p._createLoadItem = function (value, path, basePath) {
		var item = createjs.LoadItem.create(value);
		if (item == null) {
			return null;
		}

		var bp = ""; // Store the generated basePath
		var useBasePath = basePath || this._basePath;

		if (item.src instanceof Object) {
			if (!item.type) {
				return null;
			} // the the src is an object, type is required to pass off to plugin
			if (path) {
				bp = path;
				var pathMatch = createjs.RequestUtils.parseURI(path);
				// Also append basePath
				if (useBasePath != null && !pathMatch.absolute && !pathMatch.relative) {
					bp = useBasePath + bp;
				}
			} else if (useBasePath != null) {
				bp = useBasePath;
			}
		} else {
			// Determine Extension, etc.
			var match = createjs.RequestUtils.parseURI(item.src);
			if (match.extension) {
				item.ext = match.extension;
			}
			if (item.type == null) {
				item.type = createjs.RequestUtils.getTypeByExtension(item.ext);
			}

			// Inject path & basePath
			var autoId = item.src;
			if (!match.absolute && !match.relative) {
				if (path) {
					bp = path;
					var pathMatch = createjs.RequestUtils.parseURI(path);
					autoId = path + autoId;
					// Also append basePath
					if (useBasePath != null && !pathMatch.absolute && !pathMatch.relative) {
						bp = useBasePath + bp;
					}
				} else if (useBasePath != null) {
					bp = useBasePath;
				}
			}
			item.src = bp + item.src;
		}
		item.path = bp;

		// If there's no id, set one now.
		if (item.id === undefined || item.id === null || item.id === "") {
			item.id = autoId;
		}

		// Give plugins a chance to modify the loadItem:
		var customHandler = this._typeCallbacks[item.type] || this._extensionCallbacks[item.ext];
		if (customHandler) {
			// Plugins are now passed both the full source, as well as a combined path+basePath (appropriately)
			var result = customHandler.callback.call(customHandler.scope, item, this);

			// The plugin will handle the load, or has canceled it. Ignore it.
			if (result === false) {
				return null;

				// Load as normal:
			} else if (result === true) {
				// Do Nothing

				// Result is a loader class:
			} else if (result != null) {
				item._loader = result;
			}

			// Update the extension in case the type changed:
			match = createjs.RequestUtils.parseURI(item.src);
			if (match.extension != null) {
				item.ext = match.extension;
			}
		}

		// Store the item for lookup. This also helps clean-up later.
		this._loadItemsById[item.id] = item;
		this._loadItemsBySrc[item.src] = item;

		if (item.crossOrigin == null) {
			item.crossOrigin = this._crossOrigin;
		}

		return item;
	};

	/**
	 * Create a loader for a load item.
	 * @method _createLoader
	 * @param {Object} item A formatted load item that can be used to generate a loader.
	 * @return {AbstractLoader} A loader that can be used to load content.
	 * @private
	 */
	p._createLoader = function (item) {
		if (item._loader != null) { // A plugin already specified a loader
			return item._loader;
		}

		// Initially, try and use the provided/supported XHR mode:
		var preferXHR = this.preferXHR;

		for (var i = 0; i < this._availableLoaders.length; i++) {
			var loader = this._availableLoaders[i];
			if (loader && loader.canLoadItem(item)) {
				return new loader(item, preferXHR);
			}
		}

		// TODO: Log error (requires createjs.log)
		return null;
	};

	/**
	 * Load the next item in the queue. If the queue is empty (all items have been loaded), then the complete event
	 * is processed. The queue will "fill up" any empty slots, up to the max connection specified using
	 * {{#crossLink "LoadQueue.setMaxConnections"}}{{/crossLink}} method. The only exception is scripts that are loaded
	 * using tags, which have to be loaded one at a time to maintain load order.
	 * @method _loadNext
	 * @private
	 */
	p._loadNext = function () {
		if (this._paused) {
			return;
		}

		// Only dispatch loadstart event when the first file is loaded.
		if (!this._loadStartWasDispatched) {
			this._sendLoadStart();
			this._loadStartWasDispatched = true;
		}

		// The queue has completed.
		if (this._numItems == this._numItemsLoaded) {
			this.loaded = true;
			this._sendComplete();

			// Load the next queue, if it has been defined.
			if (this.next && this.next.load) {
				this.next.load();
			}
		} else {
			this.loaded = false;
		}

		// Must iterate forwards to load in the right order.
		for (var i = 0; i < this._loadQueue.length; i++) {
			if (this._currentLoads.length >= this._maxConnections) {
				break;
			}
			var loader = this._loadQueue[i];

			// Determine if we should be only loading one tag-script at a time:
			// Note: maintainOrder items don't do anything here because we can hold onto their loaded value
			if (!this._canStartLoad(loader)) {
				continue;
			}
			this._loadQueue.splice(i, 1);
			i--;
			this._loadItem(loader);
		}
	};

	/**
	 * Begin loading an item. Event listeners are not added to the loaders until the load starts.
	 * @method _loadItem
	 * @param {AbstractLoader} loader The loader instance to start. Currently, this will be an XHRLoader or TagLoader.
	 * @private
	 */
	p._loadItem = function (loader) {
		loader.on("fileload", this._handleFileLoad, this);
		loader.on("progress", this._handleProgress, this);
		loader.on("complete", this._handleFileComplete, this);
		loader.on("error", this._handleError, this);
		loader.on("fileerror", this._handleFileError, this);
		this._currentLoads.push(loader);
		this._sendFileStart(loader.getItem());
		loader.load();
	};

	/**
	 * The callback that is fired when a loader loads a file. This enables loaders like {{#crossLink "ManifestLoader"}}{{/crossLink}}
	 * to maintain internal queues, but for this queue to dispatch the {{#crossLink "fileload:event"}}{{/crossLink}}
	 * events.
	 * @param {Event} event The {{#crossLink "AbstractLoader/fileload:event"}}{{/crossLink}} event from the loader.
	 * @private
	 * @since 0.6.0
	 */
	p._handleFileLoad = function (event) {
		event.target = null;
		this.dispatchEvent(event);
	};

	/**
	 * The callback that is fired when a loader encounters an error from an internal file load operation. This enables
	 * loaders like M
	 * @param event
	 * @private
	 */
	p._handleFileError = function (event) {
		var newEvent = new createjs.ErrorEvent("FILE_LOAD_ERROR", null, event.item);
		this._sendError(newEvent);
	};

	/**
	 * The callback that is fired when a loader encounters an error. The queue will continue loading unless {{#crossLink "LoadQueue/stopOnError:property"}}{{/crossLink}}
	 * is set to `true`.
	 * @method _handleError
	 * @param {ErrorEvent} event The error event, containing relevant error information.
	 * @private
	 */
	p._handleError = function (event) {
		var loader = event.target;
		this._numItemsLoaded++;

		this._finishOrderedItem(loader, true);
		this._updateProgress();

		var newEvent = new createjs.ErrorEvent("FILE_LOAD_ERROR", null, loader.getItem());
		// TODO: Propagate actual error message.

		this._sendError(newEvent);

		if (!this.stopOnError) {
			this._removeLoadItem(loader);
			this._cleanLoadItem(loader);
			this._loadNext();
		} else {
			this.setPaused(true);
		}
	};

	/**
	 * An item has finished loading. We can assume that it is totally loaded, has been parsed for immediate use, and
	 * is available as the "result" property on the load item. The raw text result for a parsed item (such as JSON, XML,
	 * CSS, JavaScript, etc) is available as the "rawResult" property, and can also be looked up using {{#crossLink "LoadQueue/getResult"}}{{/crossLink}}.
	 * @method _handleFileComplete
	 * @param {Event} event The event object from the loader.
	 * @private
	 */
	p._handleFileComplete = function (event) {
		var loader = event.target;
		var item = loader.getItem();

		var result = loader.getResult();
		this._loadedResults[item.id] = result;
		var rawResult = loader.getResult(true);
		if (rawResult != null && rawResult !== result) {
			this._loadedRawResults[item.id] = rawResult;
		}

		this._saveLoadedItems(loader);

		// Remove the load item
		this._removeLoadItem(loader);

		if (!this._finishOrderedItem(loader)) {
			// The item was NOT managed, so process it now
			this._processFinishedLoad(item, loader);
		}

		// Clean up the load item
		this._cleanLoadItem(loader);
	};

	/**
	 * Some loaders might load additional content, other than the item they were passed (such as {{#crossLink "ManifestLoader"}}{{/crossLink}}).
	 * Any items exposed by the loader using {{#crossLink "AbstractLoader/getLoadItems"}}{{/crossLink}} are added to the
	 * LoadQueue's look-ups, including {{#crossLink "getItem"}}{{/crossLink}} and {{#crossLink "getResult"}}{{/crossLink}}
	 * methods.
	 * @method _saveLoadedItems
	 * @param {AbstractLoader} loader
	 * @protected
	 * @since 0.6.0
	 */
	p._saveLoadedItems = function (loader) {
		// TODO: Not sure how to handle this. Would be nice to expose the items.
		// Loaders may load sub-items. This adds them to this queue
		var list = loader.getLoadedItems();
		if (list === null) {
			return;
		}

		for (var i = 0; i < list.length; i++) {
			var item = list[i].item;

			// Store item lookups
			this._loadItemsBySrc[item.src] = item;
			this._loadItemsById[item.id] = item;

			// Store loaded content
			this._loadedResults[item.id] = list[i].result;
			this._loadedRawResults[item.id] = list[i].rawResult;
		}
	};

	/**
	 * Flag an item as finished. If the item's order is being managed, then ensure that it is allowed to finish, and if
	 * so, trigger prior items to trigger as well.
	 * @method _finishOrderedItem
	 * @param {AbstractLoader} loader
	 * @param {Boolean} loadFailed
	 * @return {Boolean} If the item's order is being managed. This allows the caller to take an alternate
	 * behaviour if it is.
	 * @private
	 */
	p._finishOrderedItem = function (loader, loadFailed) {
		var item = loader.getItem();

		if ((this.maintainScriptOrder && item.type == createjs.LoadQueue.JAVASCRIPT)
				|| item.maintainOrder) {

			//TODO: Evaluate removal of the _currentlyLoadingScript
			if (loader instanceof createjs.JavaScriptLoader) {
				this._currentlyLoadingScript = false;
			}

			var index = createjs.indexOf(this._scriptOrder, item);
			if (index == -1) {
				return false;
			} // This loader no longer exists
			this._loadedScripts[index] = (loadFailed === true) ? true : item;

			this._checkScriptLoadOrder();
			return true;
		}

		return false;
	};

	/**
	 * Ensure the scripts load and dispatch in the correct order. When using XHR, scripts are stored in an array in the
	 * order they were added, but with a "null" value. When they are completed, the value is set to the load item,
	 * and then when they are processed and dispatched, the value is set to `true`. This method simply
	 * iterates the array, and ensures that any loaded items that are not preceded by a `null` value are
	 * dispatched.
	 * @method _checkScriptLoadOrder
	 * @private
	 */
	p._checkScriptLoadOrder = function () {
		var l = this._loadedScripts.length;

		for (var i = 0; i < l; i++) {
			var item = this._loadedScripts[i];
			if (item === null) {
				break;
			} // This is still loading. Do not process further.
			if (item === true) {
				continue;
			} // This has completed, and been processed. Move on.

			var loadItem = this._loadedResults[item.id];
			if (item.type == createjs.LoadQueue.JAVASCRIPT) {
				// Append script tags to the head automatically.
				createjs.DomUtils.appendToHead(loadItem);
			}

			var loader = item._loader;
			this._processFinishedLoad(item, loader);
			this._loadedScripts[i] = true;
		}
	};

	/**
	 * A file has completed loading, and the LoadQueue can move on. This triggers the complete event, and kick-starts
	 * the next item.
	 * @method _processFinishedLoad
	 * @param {LoadItem|Object} item
	 * @param {AbstractLoader} loader
	 * @protected
	 */
	p._processFinishedLoad = function (item, loader) {
		this._numItemsLoaded++;

		// Since LoadQueue needs maintain order, we can't append scripts in the loader.
		// So we do it here instead. Or in _checkScriptLoadOrder();
		if (!this.maintainScriptOrder && item.type == createjs.LoadQueue.JAVASCRIPT) {
			var tag = loader.getTag();
			createjs.DomUtils.appendToHead(tag);
		}

		this._updateProgress();
		this._sendFileComplete(item, loader);
		this._loadNext();
	};

	/**
	 * Ensure items with `maintainOrder=true` that are before the specified item have loaded. This only applies to
	 * JavaScript items that are being loaded with a TagLoader, since they have to be loaded and completed <strong>before</strong>
	 * the script can even be started, since it exist in the DOM while loading.
	 * @method _canStartLoad
	 * @param {AbstractLoader} loader The loader for the item
	 * @return {Boolean} Whether the item can start a load or not.
	 * @private
	 */
	p._canStartLoad = function (loader) {
		if (!this.maintainScriptOrder || loader.preferXHR) {
			return true;
		}
		var item = loader.getItem();
		if (item.type != createjs.LoadQueue.JAVASCRIPT) {
			return true;
		}
		if (this._currentlyLoadingScript) {
			return false;
		}

		var index = this._scriptOrder.indexOf(item);
		var i = 0;
		while (i < index) {
			var checkItem = this._loadedScripts[i];
			if (checkItem == null) {
				return false;
			}
			i++;
		}
		this._currentlyLoadingScript = true;
		return true;
	};

	/**
	 * A load item is completed or was canceled, and needs to be removed from the LoadQueue.
	 * @method _removeLoadItem
	 * @param {AbstractLoader} loader A loader instance to remove.
	 * @private
	 */
	p._removeLoadItem = function (loader) {
		var l = this._currentLoads.length;
		for (var i = 0; i < l; i++) {
			if (this._currentLoads[i] == loader) {
				this._currentLoads.splice(i, 1);
				break;
			}
		}
	};

	/**
	 * Remove unneeded references from a loader.
	 *
	 * @param loader
	 * @private
	 */
	p._cleanLoadItem = function(loader) {
		var item = loader.getItem();
		if (item) {
			delete item._loader;
		}
	}

	/**
	 * An item has dispatched progress. Propagate that progress, and update the LoadQueue's overall progress.
	 * @method _handleProgress
	 * @param {ProgressEvent} event The progress event from the item.
	 * @private
	 */
	p._handleProgress = function (event) {
		var loader = event.target;
		this._sendFileProgress(loader.getItem(), loader.progress);
		this._updateProgress();
	};

	/**
	 * Overall progress has changed, so determine the new progress amount and dispatch it. This changes any time an
	 * item dispatches progress or completes. Note that since we don't always know the actual filesize of items before
	 * they are loaded. In this case, we define a "slot" for each item (1 item in 10 would get 10%), and then append
	 * loaded progress on top of the already-loaded items.
	 *
	 * For example, if 5/10 items have loaded, and item 6 is 20% loaded, the total progress would be:
	 * <ul>
	 *      <li>5/10 of the items in the queue (50%)</li>
	 *      <li>plus 20% of item 6's slot (2%)</li>
	 *      <li>equals 52%</li>
	 * </ul>
	 * @method _updateProgress
	 * @private
	 */
	p._updateProgress = function () {
		var loaded = this._numItemsLoaded / this._numItems; // Fully Loaded Progress
		var remaining = this._numItems - this._numItemsLoaded;
		if (remaining > 0) {
			var chunk = 0;
			for (var i = 0, l = this._currentLoads.length; i < l; i++) {
				chunk += this._currentLoads[i].progress;
			}
			loaded += (chunk / remaining) * (remaining / this._numItems);
		}

		if (this._lastProgress != loaded) {
			this._sendProgress(loaded);
			this._lastProgress = loaded;
		}
	};

	/**
	 * Clean out item results, to free them from memory. Mainly, the loaded item and results are cleared from internal
	 * hashes.
	 * @method _disposeItem
	 * @param {LoadItem|Object} item The item that was passed in for preloading.
	 * @private
	 */
	p._disposeItem = function (item) {
		delete this._loadedResults[item.id];
		delete this._loadedRawResults[item.id];
		delete this._loadItemsById[item.id];
		delete this._loadItemsBySrc[item.src];
	};

	/**
	 * Dispatch a "fileprogress" {{#crossLink "Event"}}{{/crossLink}}. Please see the LoadQueue {{#crossLink "LoadQueue/fileprogress:event"}}{{/crossLink}}
	 * event for details on the event payload.
	 * @method _sendFileProgress
	 * @param {LoadItem|Object} item The item that is being loaded.
	 * @param {Number} progress The amount the item has been loaded (between 0 and 1).
	 * @protected
	 */
	p._sendFileProgress = function (item, progress) {
		if (this._isCanceled() || this._paused) {
			return;
		}
		if (!this.hasEventListener("fileprogress")) {
			return;
		}

		//LM: Rework ProgressEvent to support this?
		var event = new createjs.Event("fileprogress");
		event.progress = progress;
		event.loaded = progress;
		event.total = 1;
		event.item = item;

		this.dispatchEvent(event);
	};

	/**
	 * Dispatch a fileload {{#crossLink "Event"}}{{/crossLink}}. Please see the {{#crossLink "LoadQueue/fileload:event"}}{{/crossLink}} event for
	 * details on the event payload.
	 * @method _sendFileComplete
	 * @param {LoadItemObject} item The item that is being loaded.
	 * @param {AbstractLoader} loader
	 * @protected
	 */
	p._sendFileComplete = function (item, loader) {
		if (this._isCanceled() || this._paused) {
			return;
		}

		var event = new createjs.Event("fileload");
		event.loader = loader;
		event.item = item;
		event.result = this._loadedResults[item.id];
		event.rawResult = this._loadedRawResults[item.id];

		// This calls a handler specified on the actual load item. Currently, the SoundJS plugin uses this.
		if (item.completeHandler) {
			item.completeHandler(event);
		}

		this.hasEventListener("fileload") && this.dispatchEvent(event);
	};

	/**
	 * Dispatch a filestart {{#crossLink "Event"}}{{/crossLink}} immediately before a file starts to load. Please see
	 * the {{#crossLink "LoadQueue/filestart:event"}}{{/crossLink}} event for details on the event payload.
	 * @method _sendFileStart
	 * @param {LoadItem|Object} item The item that is being loaded.
	 * @protected
	 */
	p._sendFileStart = function (item) {
		var event = new createjs.Event("filestart");
		event.item = item;
		this.hasEventListener("filestart") && this.dispatchEvent(event);
	};

	p.toString = function () {
		return "[PreloadJS LoadQueue]";
	};

	createjs.LoadQueue = createjs.promote(LoadQueue, "AbstractLoader");
}());

//##############################################################################
// TextLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for Text files.
	 * @class TextLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function TextLoader(loadItem) {
		this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.TEXT);
	};

	var p = createjs.extend(TextLoader, createjs.AbstractLoader);
	var s = TextLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader loads items that are of type {{#crossLink "AbstractLoader/TEXT:property"}}{{/crossLink}},
	 * but is also the default loader if a file type can not be determined.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.TEXT;
	};

	createjs.TextLoader = createjs.promote(TextLoader, "AbstractLoader");

}());

//##############################################################################
// BinaryLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for binary files. This is useful for loading web audio, or content that requires an ArrayBuffer.
	 * @class BinaryLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function BinaryLoader(loadItem) {
		this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.BINARY);
		this.on("initialize", this._updateXHR, this);
	};

	var p = createjs.extend(BinaryLoader, createjs.AbstractLoader);
	var s = BinaryLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/BINARY:property"}}{{/crossLink}}
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.BINARY;
	};

	// private methods
	/**
	 * Before the item loads, set the response type to "arraybuffer"
	 * @property _updateXHR
	 * @param {Event} event
	 * @private
	 */
	p._updateXHR = function (event) {
		event.loader.setResponseType("arraybuffer");
	};

	createjs.BinaryLoader = createjs.promote(BinaryLoader, "AbstractLoader");

}());

//##############################################################################
// CSSLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for CSS files.
	 * @class CSSLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @extends AbstractLoader
	 * @constructor
	 */
	function CSSLoader(loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.CSS);

		// public properties
		this.resultFormatter = this._formatResult;

		// protected properties
		this._tagSrcAttribute = "href";

		if (preferXHR) {
			this._tag = document.createElement("style");
		} else {
			this._tag = document.createElement("link");
		}

		this._tag.rel = "stylesheet";
		this._tag.type = "text/css";
	};

	var p = createjs.extend(CSSLoader, createjs.AbstractLoader);
	var s = CSSLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/CSS:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.CSS;
	};

	// protected methods
	/**
	 * The result formatter for CSS files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {HTMLLinkElement|HTMLStyleElement}
	 * @private
	 */
	p._formatResult = function (loader) {
		if (this._preferXHR) {
			var tag = loader.getTag();

			if (tag.styleSheet) { // IE
				tag.styleSheet.cssText = loader.getResult(true);
			} else {
				var textNode = document.createTextNode(loader.getResult(true));
				tag.appendChild(textNode);
			}
		} else {
			tag = this._tag;
		}

		createjs.DomUtils.appendToHead(tag);

		return tag;
	};

	createjs.CSSLoader = createjs.promote(CSSLoader, "AbstractLoader");

}());

//##############################################################################
// ImageLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for image files.
	 * @class ImageLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @extends AbstractLoader
	 * @constructor
	 */
	function ImageLoader (loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.IMAGE);

		// public properties
		this.resultFormatter = this._formatResult;

		// protected properties
		this._tagSrcAttribute = "src";

		// Check if the preload item is already a tag.
		if (createjs.RequestUtils.isImageTag(loadItem)) {
			this._tag = loadItem;
		} else if (createjs.RequestUtils.isImageTag(loadItem.src)) {
			this._tag = loadItem.src;
		} else if (createjs.RequestUtils.isImageTag(loadItem.tag)) {
			this._tag = loadItem.tag;
		}

		if (this._tag != null) {
			this._preferXHR = false;
		} else {
			this._tag = document.createElement("img");
		}

		this.on("initialize", this._updateXHR, this);
	};

	var p = createjs.extend(ImageLoader, createjs.AbstractLoader);
	var s = ImageLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/IMAGE:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.IMAGE;
	};

	// public methods
	p.load = function () {
		if (this._tag.src != "" && this._tag.complete) {
			this._sendComplete();
			return;
		}

		var crossOrigin = this._item.crossOrigin;
		if (crossOrigin == true) { crossOrigin = "Anonymous"; }
		if (crossOrigin != null && !createjs.RequestUtils.isLocal(this._item.src)) {
			this._tag.crossOrigin = crossOrigin;
		}

		this.AbstractLoader_load();
	};

	// protected methods
	/**
	 * Before the item loads, set its mimeType and responseType.
	 * @property _updateXHR
	 * @param {Event} event
	 * @private
	 */
	p._updateXHR = function (event) {
		event.loader.mimeType = 'text/plain; charset=x-user-defined-binary';

		// Only exists for XHR
		if (event.loader.setResponseType) {
			event.loader.setResponseType("blob");
		}
	};

	/**
	 * The result formatter for Image files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {HTMLImageElement}
	 * @private
	 */
	p._formatResult = function (loader) {
		return this._formatImage;
	};

	/**
	 * The asynchronous image formatter function. This is required because images have
	 * a short delay before they are ready.
	 * @method _formatImage
	 * @param {Function} successCallback The method to call when the result has finished formatting
	 * @param {Function} errorCallback The method to call if an error occurs during formatting
	 * @private
	 */
	p._formatImage = function (successCallback, errorCallback) {
		var tag = this._tag;
		var URL = window.URL || window.webkitURL;

		if (!this._preferXHR) {
			//document.body.removeChild(tag);
		} else if (URL) {
			var objURL = URL.createObjectURL(this.getResult(true));
			tag.src = objURL;

			tag.addEventListener("load", this._cleanUpURL, false);
			tag.addEventListener("error", this._cleanUpURL, false);
		} else {
			tag.src = this._item.src;
		}

		if (tag.complete) {
			successCallback(tag);
		} else {
            tag.onload = createjs.proxy(function() {
                successCallback(this._tag);
            }, this);

            tag.onerror = createjs.proxy(function() {
                errorCallback(_this._tag);
            }, this);
		}
	};

	/**
	 * Clean up the ObjectURL, the tag is done with it. Note that this function is run
	 * as an event listener without a proxy/closure, as it doesn't require it - so do not
	 * include any functionality that requires scope without changing it.
	 * @method _cleanUpURL
	 * @param event
	 * @private
	 */
	p._cleanUpURL = function (event) {
		var URL = window.URL || window.webkitURL;
		URL.revokeObjectURL(event.target.src);
	};

	createjs.ImageLoader = createjs.promote(ImageLoader, "AbstractLoader");

}());

//##############################################################################
// JavaScriptLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for JavaScript files.
	 * @class JavaScriptLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @extends AbstractLoader
	 * @constructor
	 */
	function JavaScriptLoader(loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.JAVASCRIPT);

		// public properties
		this.resultFormatter = this._formatResult;

		// protected properties
		this._tagSrcAttribute = "src";
		this.setTag(document.createElement("script"));
	};

	var p = createjs.extend(JavaScriptLoader, createjs.AbstractLoader);
	var s = JavaScriptLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/JAVASCRIPT:property"}}{{/crossLink}}
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.JAVASCRIPT;
	};

	// protected methods
	/**
	 * The result formatter for JavaScript files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {HTMLLinkElement|HTMLStyleElement}
	 * @private
	 */
	p._formatResult = function (loader) {
		var tag = loader.getTag();
		if (this._preferXHR) {
			tag.text = loader.getResult(true);
		}
		return tag;
	};

	createjs.JavaScriptLoader = createjs.promote(JavaScriptLoader, "AbstractLoader");

}());

//##############################################################################
// JSONLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for JSON files. To load JSON cross-domain, use JSONP and the {{#crossLink "JSONPLoader"}}{{/crossLink}}
	 * instead. To load JSON-formatted manifests, use {{#crossLink "ManifestLoader"}}{{/crossLink}}, and to
	 * load EaselJS SpriteSheets, use {{#crossLink "SpriteSheetLoader"}}{{/crossLink}}.
	 * @class JSONLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function JSONLoader(loadItem) {
		this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.JSON);

		// public properties
		this.resultFormatter = this._formatResult;
	};

	var p = createjs.extend(JSONLoader, createjs.AbstractLoader);
	var s = JSONLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/JSON:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.JSON;
	};

	// protected methods
	/**
	 * The result formatter for JSON files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {HTMLLinkElement|HTMLStyleElement}
	 * @private
	 */
	p._formatResult = function (loader) {
		var json = null;
		try {
			json = createjs.DataUtils.parseJSON(loader.getResult(true));
		} catch (e) {
			var event = new createjs.ErrorEvent("JSON_FORMAT", null, e);
			this._sendError(event);
			return e;
		}

		return json;
	};

	createjs.JSONLoader = createjs.promote(JSONLoader, "AbstractLoader");

}());

//##############################################################################
// JSONPLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for JSONP files, which are JSON-formatted text files, wrapped in a callback. To load regular JSON
	 * without a callback use the {{#crossLink "JSONLoader"}}{{/crossLink}} instead. To load JSON-formatted manifests,
	 * use {{#crossLink "ManifestLoader"}}{{/crossLink}}, and to load EaselJS SpriteSheets, use
	 * {{#crossLink "SpriteSheetLoader"}}{{/crossLink}}.
	 *
	 * JSONP is a format that provides a solution for loading JSON files cross-domain <em>without</em> requiring CORS.
	 * JSONP files are loaded as JavaScript, and the "callback" is executed once they are loaded. The callback in the
	 * JSONP must match the callback passed to the loadItem.
	 *
	 * <h4>Example JSONP</h4>
	 *
	 * 		callbackName({
	 * 			"name": "value",
	 *	 		"num": 3,
	 *			"obj": { "bool":true }
	 * 		});
	 *
	 * <h4>Example</h4>
	 *
	 * 		var loadItem = {id:"json", type:"jsonp", src:"http://server.com/text.json", callback:"callbackName"}
	 * 		var queue = new createjs.LoadQueue();
	 * 		queue.on("complete", handleComplete);
	 * 		queue.loadItem(loadItem);
	 *
	 * 		function handleComplete(event) }
	 * 			var json = queue.getResult("json");
	 * 			console.log(json.obj.bool); // true
	 * 		}
	 *
	 * Note that JSONP files loaded concurrently require a <em>unique</em> callback. To ensure JSONP files are loaded
	 * in order, either use the {{#crossLink "LoadQueue/setMaxConnections"}}{{/crossLink}} method (set to 1),
	 * or set {{#crossLink "LoadItem/maintainOrder:property"}}{{/crossLink}} on items with the same callback.
	 *
	 * @class JSONPLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function JSONPLoader(loadItem) {
		this.AbstractLoader_constructor(loadItem, false, createjs.AbstractLoader.JSONP);
		this.setTag(document.createElement("script"));
		this.getTag().type = "text/javascript";
	};

	var p = createjs.extend(JSONPLoader, createjs.AbstractLoader);
	var s = JSONPLoader;


	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/JSONP:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.JSONP;
	};

	// public methods
	p.cancel = function () {
		this.AbstractLoader_cancel();
		this._dispose();
	};

	/**
	 * Loads the JSONp file.  Because of the unique loading needs of JSONp
	 * we don't use the AbstractLoader.load() method.
	 *
	 * @method load
	 *
	 */
	p.load = function () {
		if (this._item.callback == null) {
			throw new Error('callback is required for loading JSONP requests.');
		}

		// TODO: Look into creating our own iFrame to handle the load
		// In the first attempt, FF did not get the result
		//   result instanceof Object did not work either
		//   so we would need to clone the result.
		if (window[this._item.callback] != null) {
			throw new Error(
				"JSONP callback '" +
				this._item.callback +
				"' already exists on window. You need to specify a different callback or re-name the current one.");
		}

		window[this._item.callback] = createjs.proxy(this._handleLoad, this);
		window.document.body.appendChild(this._tag);

		this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);

		// Load the tag
		this._tag.src = this._item.src;
	};

	// private methods
	/**
	 * Handle the JSONP callback, which is a public method defined on `window`.
	 * @method _handleLoad
	 * @param {Object} data The formatted JSON data.
	 * @private
	 */
	p._handleLoad = function (data) {
		this._result = this._rawResult = data;
		this._sendComplete();

		this._dispose();
	};

	/**
	 * The tag request has not loaded within the time specfied in loadTimeout.
	 * @method _handleError
	 * @param {Object} event The XHR error event.
	 * @private
	 */
	p._handleTimeout = function () {
		this._dispose();
		this.dispatchEvent(new createjs.ErrorEvent("timeout"));
	};

	/**
	 * Clean up the JSONP load. This clears out the callback and script tag that this loader creates.
	 * @method _dispose
	 * @private
	 */
	p._dispose = function () {
		window.document.body.removeChild(this._tag);
		delete window[this._item.callback];

		clearTimeout(this._loadTimeout);
	};

	createjs.JSONPLoader = createjs.promote(JSONPLoader, "AbstractLoader");

}());

//##############################################################################
// ManifestLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for JSON manifests. Items inside the manifest are loaded before the loader completes. To load manifests
	 * using JSONP, specify a {{#crossLink "LoadItem/callback:property"}}{{/crossLink}} as part of the
	 * {{#crossLink "LoadItem"}}{{/crossLink}}.
	 *
	 * The list of files in the manifest must be defined on the top-level JSON object in a `manifest` property. This
	 * example shows a sample manifest definition, as well as how to to include a sub-manifest.
	 *
	 * 		{
	 * 			"path": "assets/",
	 *	 	    "manifest": [
	 *				"image.png",
	 *				{"src": "image2.png", "id":"image2"},
	 *				{"src": "sub-manifest.json", "type":"manifest", "callback":"jsonCallback"}
	 *	 	    ]
	 *	 	}
	 *
	 * When a ManifestLoader has completed loading, the parent loader (usually a {{#crossLink "LoadQueue"}}{{/crossLink}},
	 * but could also be another ManifestLoader) will inherit all the loaded items, so you can access them directly.
	 *
	 * Note that the {{#crossLink "JSONLoader"}}{{/crossLink}} and {{#crossLink "JSONPLoader"}}{{/crossLink}} are
	 * higher priority loaders, so manifests <strong>must</strong> set the {{#crossLink "LoadItem"}}{{/crossLink}}
	 * {{#crossLink "LoadItem/type:property"}}{{/crossLink}} property to {{#crossLink "AbstractLoader/MANIFEST:property"}}{{/crossLink}}.
	 * @class ManifestLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function ManifestLoader(loadItem) {
		this.AbstractLoader_constructor(loadItem, null, createjs.AbstractLoader.MANIFEST);

	// Public Properties
		/**
		 * An array of the plugins registered using {{#crossLink "LoadQueue/installPlugin"}}{{/crossLink}},
		 * used to pass plugins to new LoadQueues that may be created.
		 * @property _plugins
		 * @type {Array}
		 * @private
		 * @since 0.6.1
		 */
		this.plugins = null;


	// Protected Properties
		/**
		 * An internal {{#crossLink "LoadQueue"}}{{/crossLink}} that loads the contents of the manifest.
		 * @property _manifestQueue
		 * @type {LoadQueue}
		 * @private
		 */
		this._manifestQueue = null;
	};

	var p = createjs.extend(ManifestLoader, createjs.AbstractLoader);
	var s = ManifestLoader;

	// static properties
	/**
	 * The amount of progress that the manifest itself takes up.
	 * @property MANIFEST_PROGRESS
	 * @type {number}
	 * @default 0.25 (25%)
	 * @private
	 * @static
	 */
	s.MANIFEST_PROGRESS = 0.25;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/MANIFEST:property"}}{{/crossLink}}
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.MANIFEST;
	};

	// public methods
	p.load = function () {
		this.AbstractLoader_load();
	};

	// protected methods
	p._createRequest = function() {
		var callback = this._item.callback;
		if (callback != null) {
			this._request = new createjs.JSONPLoader(this._item);
		} else {
			this._request = new createjs.JSONLoader(this._item);
		}
	};

	p.handleEvent = function (event) {
		switch (event.type) {
			case "complete":
				this._rawResult = event.target.getResult(true);
				this._result = event.target.getResult();
				this._sendProgress(s.MANIFEST_PROGRESS);
				this._loadManifest(this._result);
				return;
			case "progress":
				event.loaded *= s.MANIFEST_PROGRESS;
				this.progress = event.loaded / event.total;
				if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
				this._sendProgress(event);
				return;
		}
		this.AbstractLoader_handleEvent(event);
	};

	p.destroy = function() {
		this.AbstractLoader_destroy();
		this._manifestQueue.close();
	};

	/**
	 * Create and load the manifest items once the actual manifest has been loaded.
	 * @method _loadManifest
	 * @param {Object} json
	 * @private
	 */
	p._loadManifest = function (json) {
		if (json && json.manifest) {
			var queue = this._manifestQueue = new createjs.LoadQueue();
			queue.on("fileload", this._handleManifestFileLoad, this);
			queue.on("progress", this._handleManifestProgress, this);
			queue.on("complete", this._handleManifestComplete, this, true);
			queue.on("error", this._handleManifestError, this, true);
			for(var i = 0, l = this.plugins.length; i < l; i++) {	// conserve order of plugins
				queue.installPlugin(this.plugins[i]);
			}
			queue.loadManifest(json);
		} else {
			this._sendComplete();
		}
	};

	/**
	 * An item from the {{#crossLink "_manifestQueue:property"}}{{/crossLink}} has completed.
	 * @method _handleManifestFileLoad
	 * @param {Event} event
	 * @private
	 */
	p._handleManifestFileLoad = function (event) {
		event.target = null;
		this.dispatchEvent(event);
	};

	/**
	 * The manifest has completed loading. This triggers the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}}
	 * {{#crossLink "Event"}}{{/crossLink}} from the ManifestLoader.
	 * @method _handleManifestComplete
	 * @param {Event} event
	 * @private
	 */
	p._handleManifestComplete = function (event) {
		this._loadedItems = this._manifestQueue.getItems(true);
		this._sendComplete();
	};

	/**
	 * The manifest has reported progress.
	 * @method _handleManifestProgress
	 * @param {ProgressEvent} event
	 * @private
	 */
	p._handleManifestProgress = function (event) {
		this.progress = event.progress * (1 - s.MANIFEST_PROGRESS) + s.MANIFEST_PROGRESS;
		this._sendProgress(this.progress);
	};

	/**
	 * The manifest has reported an error with one of the files.
	 * @method _handleManifestError
	 * @param {ErrorEvent} event
	 * @private
	 */
	p._handleManifestError = function (event) {
		var newEvent = new createjs.Event("fileerror");
		newEvent.item = event.data;
		this.dispatchEvent(newEvent);
	};

	createjs.ManifestLoader = createjs.promote(ManifestLoader, "AbstractLoader");

}());

//##############################################################################
// SoundLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for HTML audio files. PreloadJS can not load WebAudio files, as a WebAudio context is required, which
	 * should be created by either a library playing the sound (such as <a href="http://soundjs.com">SoundJS</a>, or an
	 * external framework that handles audio playback. To load content that can be played by WebAudio, use the
	 * {{#crossLink "BinaryLoader"}}{{/crossLink}}, and handle the audio context decoding manually.
	 * @class SoundLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @extends AbstractMediaLoader
	 * @constructor
	 */
	function SoundLoader(loadItem, preferXHR) {
		this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SOUND);

		// protected properties
		if (createjs.RequestUtils.isAudioTag(loadItem)) {
			this._tag = loadItem;
		} else if (createjs.RequestUtils.isAudioTag(loadItem.src)) {
			this._tag = loadItem;
		} else if (createjs.RequestUtils.isAudioTag(loadItem.tag)) {
			this._tag = createjs.RequestUtils.isAudioTag(loadItem) ? loadItem : loadItem.src;
		}

		if (this._tag != null) {
			this._preferXHR = false;
		}
	};

	var p = createjs.extend(SoundLoader, createjs.AbstractMediaLoader);
	var s = SoundLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/SOUND:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.SOUND;
	};

	// protected methods
	p._createTag = function (src) {
		var tag = document.createElement("audio");
		tag.autoplay = false;
		tag.preload = "none";

		//LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
		tag.src = src;
		return tag;
	};

	createjs.SoundLoader = createjs.promote(SoundLoader, "AbstractMediaLoader");

}());

//##############################################################################
// VideoLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for video files.
	 * @class VideoLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @extends AbstractMediaLoader
	 * @constructor
	 */
	function VideoLoader(loadItem, preferXHR) {
		this.AbstractMediaLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.VIDEO);

		if (createjs.RequestUtils.isVideoTag(loadItem) || createjs.RequestUtils.isVideoTag(loadItem.src)) {
			this.setTag(createjs.RequestUtils.isVideoTag(loadItem)?loadItem:loadItem.src);

			// We can't use XHR for a tag that's passed in.
			this._preferXHR = false;
		} else {
			this.setTag(this._createTag());
		}
	};

	var p = createjs.extend(VideoLoader, createjs.AbstractMediaLoader);
	var s = VideoLoader;

	/**
	 * Create a new video tag
	 *
	 * @returns {HTMLElement}
	 * @private
	 */
	p._createTag = function () {
		return document.createElement("video");
	};

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/VIDEO:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.VIDEO;
	};

	createjs.VideoLoader = createjs.promote(VideoLoader, "AbstractMediaLoader");

}());

//##############################################################################
// SpriteSheetLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for EaselJS SpriteSheets. Images inside the spritesheet definition are loaded before the loader
	 * completes. To load SpriteSheets using JSONP, specify a {{#crossLink "LoadItem/callback:property"}}{{/crossLink}}
	 * as part of the {{#crossLink "LoadItem"}}{{/crossLink}}. Note that the {{#crossLink "JSONLoader"}}{{/crossLink}}
	 * and {{#crossLink "JSONPLoader"}}{{/crossLink}} are higher priority loaders, so SpriteSheets <strong>must</strong>
	 * set the {{#crossLink "LoadItem"}}{{/crossLink}} {{#crossLink "LoadItem/type:property"}}{{/crossLink}} property
	 * to {{#crossLink "AbstractLoader/SPRITESHEET:property"}}{{/crossLink}}.
	 *
	 * The {{#crossLink "LoadItem"}}{{/crossLink}} {{#crossLink "LoadItem/crossOrigin:property"}}{{/crossLink}} as well
	 * as the {{#crossLink "LoadQueue's"}}{{/crossLink}} `basePath` argument and {{#crossLink "LoadQueue/_preferXHR"}}{{/crossLink}}
	 * property supplied to the {{#crossLink "LoadQueue"}}{{/crossLink}} are passed on to the sub-manifest that loads
	 * the SpriteSheet images.
	 *
	 * Note that the SpriteSheet JSON does not respect the {{#crossLink "LoadQueue/_preferXHR:property"}}{{/crossLink}}
	 * property, which should instead be determined by the presence of a {{#crossLink "LoadItem/callback:property"}}{{/crossLink}}
	 * property on the SpriteSheet load item. This is because the JSON loaded will have a different format depending on
	 * if it is loaded as JSON, so just changing `preferXHR` is not enough to change how it is loaded.
	 * @class SpriteSheetLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function SpriteSheetLoader(loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SPRITESHEET);

		// protected properties
		/**
		 * An internal queue which loads the SpriteSheet's images.
		 * @method _manifestQueue
		 * @type {LoadQueue}
		 * @private
		 */
		this._manifestQueue = null;
	}

	var p = createjs.extend(SpriteSheetLoader, createjs.AbstractLoader);
	var s = SpriteSheetLoader;

	// static properties
	/**
	 * The amount of progress that the manifest itself takes up.
	 * @property SPRITESHEET_PROGRESS
	 * @type {number}
	 * @default 0.25 (25%)
	 * @private
	 * @static
	 */
	s.SPRITESHEET_PROGRESS = 0.25;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/SPRITESHEET:property"}}{{/crossLink}}
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.SPRITESHEET;
	};

	// public methods
	p.destroy = function() {
		this.AbstractLoader_destroy;
		this._manifestQueue.close();
	};

	// protected methods
	p._createRequest = function() {
		var callback = this._item.callback;
		if (callback != null) {
			this._request = new createjs.JSONPLoader(this._item);
		} else {
			this._request = new createjs.JSONLoader(this._item);
		}
	};

	p.handleEvent = function (event) {
		switch (event.type) {
			case "complete":
				this._rawResult = event.target.getResult(true);
				this._result = event.target.getResult();
				this._sendProgress(s.SPRITESHEET_PROGRESS);
				this._loadManifest(this._result);
				return;
			case "progress":
				event.loaded *= s.SPRITESHEET_PROGRESS;
				this.progress = event.loaded / event.total;
				if (isNaN(this.progress) || this.progress == Infinity) { this.progress = 0; }
				this._sendProgress(event);
				return;
		}
		this.AbstractLoader_handleEvent(event);
	};

	/**
	 * Create and load the images once the SpriteSheet JSON has been loaded.
	 * @method _loadManifest
	 * @param {Object} json
	 * @private
	 */
	p._loadManifest = function (json) {
		if (json && json.images) {
			var queue = this._manifestQueue = new createjs.LoadQueue(this._preferXHR, this._item.path, this._item.crossOrigin);
			queue.on("complete", this._handleManifestComplete, this, true);
			queue.on("fileload", this._handleManifestFileLoad, this);
			queue.on("progress", this._handleManifestProgress, this);
			queue.on("error", this._handleManifestError, this, true);
			queue.loadManifest(json.images);
		}
	};

	/**
	 * An item from the {{#crossLink "_manifestQueue:property"}}{{/crossLink}} has completed.
	 * @method _handleManifestFileLoad
	 * @param {Event} event
	 * @private
	 */
	p._handleManifestFileLoad = function (event) {
		var image = event.result;
		if (image != null) {
			var images = this.getResult().images;
			var pos = images.indexOf(event.item.src);
			images[pos] = image;
		}
	};

	/**
	 * The images have completed loading. This triggers the {{#crossLink "AbstractLoader/complete:event"}}{{/crossLink}}
	 * {{#crossLink "Event"}}{{/crossLink}} from the SpriteSheetLoader.
	 * @method _handleManifestComplete
	 * @param {Event} event
	 * @private
	 */
	p._handleManifestComplete = function (event) {
		this._result = new createjs.SpriteSheet(this._result);
		this._loadedItems = this._manifestQueue.getItems(true);
		this._sendComplete();
	};

	/**
	 * The images {{#crossLink "LoadQueue"}}{{/crossLink}} has reported progress.
	 * @method _handleManifestProgress
	 * @param {ProgressEvent} event
	 * @private
	 */
	p._handleManifestProgress = function (event) {
		this.progress = event.progress * (1 - s.SPRITESHEET_PROGRESS) + s.SPRITESHEET_PROGRESS;
		this._sendProgress(this.progress);
	};

	/**
	 * An image has reported an error.
	 * @method _handleManifestError
	 * @param {ErrorEvent} event
	 * @private
	 */
	p._handleManifestError = function (event) {
		var newEvent = new createjs.Event("fileerror");
		newEvent.item = event.data;
		this.dispatchEvent(newEvent);
	};

	createjs.SpriteSheetLoader = createjs.promote(SpriteSheetLoader, "AbstractLoader");

}());

//##############################################################################
// SVGLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for SVG files.
	 * @class SVGLoader
	 * @param {LoadItem|Object} loadItem
	 * @param {Boolean} preferXHR
	 * @extends AbstractLoader
	 * @constructor
	 */
	function SVGLoader(loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, createjs.AbstractLoader.SVG);

		// public properties
		this.resultFormatter = this._formatResult;

		// protected properties
		this._tagSrcAttribute = "data";

		if (preferXHR) {
			this.setTag(document.createElement("svg"));
		} else {
			this.setTag(document.createElement("object"));
			this.getTag().type = "image/svg+xml";
		}
	};

	var p = createjs.extend(SVGLoader, createjs.AbstractLoader);
	var s = SVGLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/SVG:property"}}{{/crossLink}}
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.SVG;
	};

	// protected methods
	/**
	 * The result formatter for SVG files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {Object}
	 * @private
	 */
	p._formatResult = function (loader) {
		// mime should be image/svg+xml, but Opera requires text/xml
		var xml = createjs.DataUtils.parseXML(loader.getResult(true), "text/xml");
		var tag = loader.getTag();

		if (!this._preferXHR && document.body.contains(tag)) {
			document.body.removeChild(tag);
		}

		if (xml.documentElement != null) {
			tag.appendChild(xml.documentElement);
			tag.style.visibility = "visible";
			return tag;
		} else { // For browsers that don't support SVG, just give them the XML. (IE 9-8)
			return xml;
		}
	};

	createjs.SVGLoader = createjs.promote(SVGLoader, "AbstractLoader");

}());

//##############################################################################
// XMLLoader.js
//##############################################################################

(function () {
	"use strict";

	// constructor
	/**
	 * A loader for CSS files.
	 * @class XMLLoader
	 * @param {LoadItem|Object} loadItem
	 * @extends AbstractLoader
	 * @constructor
	 */
	function XMLLoader(loadItem) {
		this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.XML);

		// public properties
		this.resultFormatter = this._formatResult;
	};

	var p = createjs.extend(XMLLoader, createjs.AbstractLoader);
	var s = XMLLoader;

	// static methods
	/**
	 * Determines if the loader can load a specific item. This loader can only load items that are of type
	 * {{#crossLink "AbstractLoader/XML:property"}}{{/crossLink}}.
	 * @method canLoadItem
	 * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
	 * @returns {Boolean} Whether the loader can load the item.
	 * @static
	 */
	s.canLoadItem = function (item) {
		return item.type == createjs.AbstractLoader.XML;
	};

	// protected methods
	/**
	 * The result formatter for XML files.
	 * @method _formatResult
	 * @param {AbstractLoader} loader
	 * @returns {XMLDocument}
	 * @private
	 */
	p._formatResult = function (loader) {
		return createjs.DataUtils.parseXML(loader.getResult(true), "text/xml");
	};

	createjs.XMLLoader = createjs.promote(XMLLoader, "AbstractLoader");

}());

//##############################################################################
// version.js
//##############################################################################

(function () {

	/**
	 * Static class holding library specific information such as the version and buildDate of the library.
	 * The SoundJS class has been renamed {{#crossLink "Sound"}}{{/crossLink}}.  Please see {{#crossLink "Sound"}}{{/crossLink}}
	 * for information on using sound.
	 * @class SoundJS
	 **/
	var s = createjs.SoundJS = createjs.SoundJS || {};

	/**
	 * The version string for this release.
	 * @property version
	 * @type String
	 * @static
	 **/
	s.version = /*=version*/"0.6.2"; // injected by build process

	/**
	 * The build date for this release in UTC format.
	 * @property buildDate
	 * @type String
	 * @static
	 **/
	s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:31 GMT"; // injected by build process

})();

//##############################################################################
// IndexOf.js
//##############################################################################

/**
 * @class Utility Methods
 */

/**
 * Finds the first occurrence of a specified value searchElement in the passed in array, and returns the index of
 * that value.  Returns -1 if value is not found.
 *
 *      var i = createjs.indexOf(myArray, myElementToFind);
 *
 * @method indexOf
 * @param {Array} array Array to search for searchElement
 * @param searchElement Element to find in array.
 * @return {Number} The first index of searchElement in array.
 */
createjs.indexOf = function (array, searchElement){
	"use strict";

	for (var i = 0,l=array.length; i < l; i++) {
		if (searchElement === array[i]) {
			return i;
		}
	}
	return -1;
};

//##############################################################################
// Proxy.js
//##############################################################################

/**
 * Various utilities that the CreateJS Suite uses. Utilities are created as separate files, and will be available on the
 * createjs namespace directly.
 *
 * <h4>Example</h4>
 *
 *      myObject.addEventListener("change", createjs.proxy(myMethod, scope));
 *
 * @class Utility Methods
 * @main Utility Methods
 */

(function() {
	"use strict";

	/**
	 * A function proxy for methods. By default, JavaScript methods do not maintain scope, so passing a method as a
	 * callback will result in the method getting called in the scope of the caller. Using a proxy ensures that the
	 * method gets called in the correct scope.
	 *
	 * Additional arguments can be passed that will be applied to the function when it is called.
	 *
	 * <h4>Example</h4>
	 *
	 *      myObject.addEventListener("event", createjs.proxy(myHandler, this, arg1, arg2));
	 *
	 *      function myHandler(arg1, arg2) {
	 *           // This gets called when myObject.myCallback is executed.
	 *      }
	 *
	 * @method proxy
	 * @param {Function} method The function to call
	 * @param {Object} scope The scope to call the method name on
	 * @param {mixed} [arg] * Arguments that are appended to the callback for additional params.
	 * @public
	 * @static
	 */
	createjs.proxy = function (method, scope) {
		var aArgs = Array.prototype.slice.call(arguments, 2);
		return function () {
			return method.apply(scope, Array.prototype.slice.call(arguments, 0).concat(aArgs));
		};
	}

}());

//##############################################################################
// BrowserDetect.js
//##############################################################################

/**
 * @class Utility Methods
 */
(function() {
	"use strict";

	/**
	 * An object that determines the current browser, version, operating system, and other environment
	 * variables via user agent string.
	 *
	 * Used for audio because feature detection is unable to detect the many limitations of mobile devices.
	 *
	 * <h4>Example</h4>
	 *
	 *      if (createjs.BrowserDetect.isIOS) { // do stuff }
	 *
	 * @property BrowserDetect
	 * @type {Object}
	 * @param {Boolean} isFirefox True if our browser is Firefox.
	 * @param {Boolean} isOpera True if our browser is opera.
	 * @param {Boolean} isChrome True if our browser is Chrome.  Note that Chrome for Android returns true, but is a
	 * completely different browser with different abilities.
	 * @param {Boolean} isIOS True if our browser is safari for iOS devices (iPad, iPhone, and iPod).
	 * @param {Boolean} isAndroid True if our browser is Android.
	 * @param {Boolean} isBlackberry True if our browser is Blackberry.
	 * @constructor
	 * @static
	 */
	function BrowserDetect() {
		throw "BrowserDetect cannot be instantiated";
	};

	var agent = BrowserDetect.agent = window.navigator.userAgent;
	BrowserDetect.isWindowPhone = (agent.indexOf("IEMobile") > -1) || (agent.indexOf("Windows Phone") > -1);
	BrowserDetect.isFirefox = (agent.indexOf("Firefox") > -1);
	BrowserDetect.isOpera = (window.opera != null);
	BrowserDetect.isChrome = (agent.indexOf("Chrome") > -1);  // NOTE that Chrome on Android returns true but is a completely different browser with different abilities
	BrowserDetect.isIOS = (agent.indexOf("iPod") > -1 || agent.indexOf("iPhone") > -1 || agent.indexOf("iPad") > -1) && !BrowserDetect.isWindowPhone;
	BrowserDetect.isAndroid = (agent.indexOf("Android") > -1) && !BrowserDetect.isWindowPhone;
	BrowserDetect.isBlackberry = (agent.indexOf("Blackberry") > -1);

	createjs.BrowserDetect = BrowserDetect;

}());

//##############################################################################
// AudioSprite.js
//##############################################################################

//  NOTE this is "Class" is purely to document audioSprite Setup and usage.


/**
 * <strong>Note: AudioSprite is not a class, but its usage is easily lost in the documentation, so it has been called
 * out here for quick reference.</strong>
 *
 * Audio sprites are much like CSS sprites or image sprite sheets: multiple audio assets grouped into a single file.
 * Audio sprites work around limitations in certain browsers, where only a single sound can be loaded and played at a
 * time. We recommend at least 300ms of silence between audio clips to deal with HTML audio tag inaccuracy, and to prevent
 * accidentally playing bits of the neighbouring clips.
 *
 * <strong>Benefits of Audio Sprites:</strong>
 * <ul>
 *     <li>More robust support for older browsers and devices that only allow a single audio instance, such as iOS 5.</li>
 *     <li>They provide a work around for the Internet Explorer 9 audio tag limit, which restricts how many different
 *     sounds that could be loaded at once.</li>
 *     <li>Faster loading by only requiring a single network request for several sounds, especially on mobile devices
 * where the network round trip for each file can add significant latency.</li>
 * </ul>
 *
 * <strong>Drawbacks of Audio Sprites</strong>
 * <ul>
 *     <li>No guarantee of smooth looping when using HTML or Flash audio. If you have a track that needs to loop
 * 		smoothly and you are supporting non-web audio browsers, do not use audio sprites for that sound if you can avoid
 * 		it.</li>
 *     <li>No guarantee that HTML audio will play back immediately, especially the first time. In some browsers
 *     (Chrome!), HTML audio will only load enough to play through at the current download speed – so we rely on the
 *     `canplaythrough` event to determine if the audio is loaded. Since audio sprites must jump ahead to play specific
 *     sounds, the audio may not yet have downloaded fully.</li>
 *     <li>Audio sprites share the same core source, so if you have a sprite with 5 sounds and are limited to 2
 * 		concurrently playing instances, you can only play 2 of the sounds at the same time.</li>
 * </ul>
 *
 * <h4>Example</h4>
 *
 *		createjs.Sound.initializeDefaultPlugins();
 *		var assetsPath = "./assets/";
 *		var sounds = [{
 *			src:"MyAudioSprite.ogg", data: {
 *				audioSprite: [
 *					{id:"sound1", startTime:0, duration:500},
 *					{id:"sound2", startTime:1000, duration:400},
 *					{id:"sound3", startTime:1700, duration: 1000}
 *				]}
 *			}
 *		];
 *		createjs.Sound.alternateExtensions = ["mp3"];
 *		createjs.Sound.on("fileload", loadSound);
 *		createjs.Sound.registerSounds(sounds, assetsPath);
 *		// after load is complete
 *		createjs.Sound.play("sound2");
 *
 * You can also create audio sprites on the fly by setting the startTime and duration when creating an new AbstractSoundInstance.
 *
 * 		createjs.Sound.play("MyAudioSprite", {startTime: 1000, duration: 400});
 *
 * The excellent CreateJS community has created a tool to create audio sprites, available at
 * <a href="https://github.com/tonistiigi/audiosprite" target="_blank">https://github.com/tonistiigi/audiosprite</a>,
 * as well as a <a href="http://jsfiddle.net/bharat_battu/g8fFP/12/" target="_blank">jsfiddle</a> to convert the output
 * to SoundJS format.
 *
 * @class AudioSprite
 * @since 0.6.0
 */

//##############################################################################
// PlayPropsConfig.js
//##############################################################################

(function () {
	"use strict";
	/**
	 * A class to store the optional play properties passed in {{#crossLink "Sound/play"}}{{/crossLink}} and
	 * {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} calls.
	 *
	 * Optional Play Properties Include:
	 * <ul>
	 * <li>interrupt - How to interrupt any currently playing instances of audio with the same source,
	 * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
	 * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.</li>
	 * <li>delay - The amount of time to delay the start of audio playback, in milliseconds.</li>
	 * <li>offset - The offset from the start of the audio to begin playback, in milliseconds.</li>
	 * <li>loop - How many times the audio loops when it reaches the end of playback. The default is 0 (no
	 * loops), and -1 can be used for infinite playback.</li>
	 * <li>volume - The volume of the sound, between 0 and 1. Note that the master volume is applied
	 * against the individual volume.</li>
	 * <li>pan - The left-right pan of the sound (if supported), between -1 (left) and 1 (right).</li>
	 * <li>startTime - To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.</li>
	 * <li>duration - To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.</li>
	 * </ul>
	 *
	 * <h4>Example</h4>
	 *
	 * 	var ppc = new createjs.PlayPropsConfig().set({interrupt: createjs.Sound.INTERRUPT_ANY, loop: -1, volume: 0.5})
	 * 	createjs.Sound.play("mySound", ppc);
	 * 	mySoundInstance.play(ppc);
	 *
	 * @class PlayPropsConfig
	 * @constructor
	 * @since 0.6.1
	 */
	// TODO think of a better name for this class
	var PlayPropsConfig = function () {
// Public Properties
		/**
		 * How to interrupt any currently playing instances of audio with the same source,
		 * if the maximum number of instances of the sound are already playing. Values are defined as
		 * <code>INTERRUPT_TYPE</code> constants on the Sound class, with the default defined by
		 * {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
		 * @property interrupt
		 * @type {string}
		 * @default null
		 */
		this.interrupt = null;

		/**
		 * The amount of time to delay the start of audio playback, in milliseconds.
		 * @property delay
		 * @type {Number}
		 * @default null
		 */
		this.delay = null;

		/**
		 * The offset from the start of the audio to begin playback, in milliseconds.
		 * @property offset
		 * @type {number}
		 * @default null
		 */
		this.offset = null;

		/**
		 * How many times the audio loops when it reaches the end of playback. The default is 0 (no
		 * loops), and -1 can be used for infinite playback.
		 * @property loop
		 * @type {number}
		 * @default null
		 */
		this.loop = null;

		/**
		 * The volume of the sound, between 0 and 1. Note that the master volume is applied
		 * against the individual volume.
		 * @property volume
		 * @type {number}
		 * @default null
		 */
		this.volume = null;

		/**
		 * The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
		 * @property pan
		 * @type {number}
		 * @default null
		 */
		this.pan = null;

		/**
		 * Used to create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
		 * @property startTime
		 * @type {number}
		 * @default null
		 */
		this.startTime = null;

		/**
		 * Used to create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
		 * @property duration
		 * @type {number}
		 * @default null
		 */
		this.duration = null;
	};
	var p = PlayPropsConfig.prototype = {};
	var s = PlayPropsConfig;


// Static Methods
	/**
	 * Creates a PlayPropsConfig from another PlayPropsConfig or an Object.
	 *
	 * @method create
	 * @param {PlayPropsConfig|Object} value The play properties
	 * @returns {PlayPropsConfig}
	 * @static
	 */
	s.create = function (value) {
		if (value instanceof s || value instanceof Object) {
			var ppc = new createjs.PlayPropsConfig();
			ppc.set(value);
			return ppc;
		} else {
			throw new Error("Type not recognized.");
		}
	};

// Public Methods
	/**
	 * Provides a chainable shortcut method for setting a number of properties on the instance.
	 *
	 * <h4>Example</h4>
	 *
	 *      var PlayPropsConfig = new createjs.PlayPropsConfig().set({loop:-1, volume:0.7});
	 *
	 * @method set
	 * @param {Object} props A generic object containing properties to copy to the PlayPropsConfig instance.
	 * @return {PlayPropsConfig} Returns the instance the method is called on (useful for chaining calls.)
	*/
	p.set = function(props) {
		for (var n in props) { this[n] = props[n]; }
		return this;
	};

	p.toString = function() {
		return "[PlayPropsConfig]";
	};

	createjs.PlayPropsConfig = s;

}());

//##############################################################################
// Sound.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * The Sound class is the public API for creating sounds, controlling the overall sound levels, and managing plugins.
	 * All Sound APIs on this class are static.
	 *
	 * <b>Registering and Preloading</b><br />
	 * Before you can play a sound, it <b>must</b> be registered. You can do this with {{#crossLink "Sound/registerSound"}}{{/crossLink}},
	 * or register multiple sounds using {{#crossLink "Sound/registerSounds"}}{{/crossLink}}. If you don't register a
	 * sound prior to attempting to play it using {{#crossLink "Sound/play"}}{{/crossLink}} or create it using {{#crossLink "Sound/createInstance"}}{{/crossLink}},
	 * the sound source will be automatically registered but playback will fail as the source will not be ready. If you use
	 * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>, registration is handled for you when the sound is
	 * preloaded. It is recommended to preload sounds either internally using the register functions or externally using
	 * PreloadJS so they are ready when you want to use them.
	 *
	 * <b>Playback</b><br />
	 * To play a sound once it's been registered and preloaded, use the {{#crossLink "Sound/play"}}{{/crossLink}} method.
	 * This method returns a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} which can be paused, resumed, muted, etc.
	 * Please see the {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} documentation for more on the instance control APIs.
	 *
	 * <b>Plugins</b><br />
	 * By default, the {{#crossLink "WebAudioPlugin"}}{{/crossLink}} or the {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}
	 * are used (when available), although developers can change plugin priority or add new plugins (such as the
	 * provided {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}). Please see the {{#crossLink "Sound"}}{{/crossLink}} API
	 * methods for more on the playback and plugin APIs. To install plugins, or specify a different plugin order, see
	 * {{#crossLink "Sound/installPlugins"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio";
	 *      createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashAudioPlugin]);
	 *      createjs.Sound.alternateExtensions = ["mp3"];
	 *      createjs.Sound.on("fileload", this.loadHandler, this);
	 *      createjs.Sound.registerSound("path/to/mySound.ogg", "sound");
	 *      function loadHandler(event) {
     *          // This is fired for each sound that is registered.
     *          var instance = createjs.Sound.play("sound");  // play using id.  Could also use full source path or event.src.
     *          instance.on("complete", this.handleComplete, this);
     *          instance.volume = 0.5;
	 *      }
	 *
	 * The maximum number of concurrently playing instances of the same sound can be specified in the "data" argument
	 * of {{#crossLink "Sound/registerSound"}}{{/crossLink}}.  Note that if not specified, the active plugin will apply
	 * a default limit.  Currently HTMLAudioPlugin sets a default limit of 2, while WebAudioPlugin and FlashAudioPlugin set a
	 * default limit of 100.
	 *
	 *      createjs.Sound.registerSound("sound.mp3", "soundId", 4);
	 *
	 * Sound can be used as a plugin with PreloadJS to help preload audio properly. Audio preloaded with PreloadJS is
	 * automatically registered with the Sound class. When audio is not preloaded, Sound will do an automatic internal
	 * load. As a result, it may fail to play the first time play is called if the audio is not finished loading. Use
	 * the {{#crossLink "Sound/fileload:event"}}{{/crossLink}} event to determine when a sound has finished internally
	 * preloading. It is recommended that all audio is preloaded before it is played.
	 *
	 *      var queue = new createjs.LoadQueue();
	 *		queue.installPlugin(createjs.Sound);
	 *
	 * <b>Audio Sprites</b><br />
	 * SoundJS has added support for {{#crossLink "AudioSprite"}}{{/crossLink}}, available as of version 0.6.0.
	 * For those unfamiliar with audio sprites, they are much like CSS sprites or sprite sheets: multiple audio assets
	 * grouped into a single file.
	 *
	 * <h4>Example</h4>
	 *
	 *		var assetsPath = "./assets/";
	 *		var sounds = [{
	 *			src:"MyAudioSprite.ogg", data: {
	 *				audioSprite: [
	 *					{id:"sound1", startTime:0, duration:500},
	 *					{id:"sound2", startTime:1000, duration:400},
	 *					{id:"sound3", startTime:1700, duration: 1000}
	 *				]}
 	 *			}
	 *		];
	 *		createjs.Sound.alternateExtensions = ["mp3"];
	 *		createjs.Sound.on("fileload", loadSound);
	 *		createjs.Sound.registerSounds(sounds, assetsPath);
	 *		// after load is complete
	 *		createjs.Sound.play("sound2");
	 *
	 * <b>Mobile Playback</b><br />
	 * Devices running iOS require the WebAudio context to be "unlocked" by playing at least one sound inside of a user-
	 * initiated event (such as touch/click). Earlier versions of SoundJS included a "MobileSafe" sample, but this is no
	 * longer necessary as of SoundJS 0.6.2.
	 * <ul>
	 *     <li>
	 *         In SoundJS 0.4.1 and above, you can either initialize plugins or use the {{#crossLink "WebAudioPlugin/playEmptySound"}}{{/crossLink}}
	 *         method in the call stack of a user input event to manually unlock the audio context.
	 *     </li>
	 *     <li>
	 *         In SoundJS 0.6.2 and above, SoundJS will automatically listen for the first document-level "mousedown"
	 *         and "touchend" event, and unlock WebAudio. This will continue to check these events until the WebAudio
	 *         context becomes "unlocked" (changes from "suspended" to "running")
	 *     </li>
	 *     <li>
	 *         Both the "mousedown" and "touchend" events can be used to unlock audio in iOS9+, the "touchstart" event
	 *         will work in iOS8 and below. The "touchend" event will only work in iOS9 when the gesture is interpreted
	 *         as a "click", so if the user long-presses the button, it will no longer work.
	 *     </li>
	 *     <li>
	 *         When using the <a href="http://www.createjs.com/docs/easeljs/classes/Touch.html">EaselJS Touch class</a>,
	 *         the "mousedown" event will not fire when a canvas is clicked, since MouseEvents are prevented, to ensure
	 *         only touch events fire. To get around this, you can either rely on "touchend", or:
	 *         <ol>
	 *             <li>Set the `allowDefault` property on the Touch class constructor to `true` (defaults to `false`).</li>
	 *             <li>Set the `preventSelection` property on the EaselJS `Stage` to `false`.</li>
	 *         </ol>
	 *         These settings may change how your application behaves, and are not recommended.
	 *     </li>
	 * </ul>
	 *
	 * <b>Loading Alternate Paths and Extension-less Files</b><br />
	 * SoundJS supports loading alternate paths and extension-less files by passing an object instead of a string for
	 * the `src` property, which is a hash using the format `{extension:"path", extension2:"path2"}`. These labels are
	 * how SoundJS determines if the browser will support the sound. This also enables multiple formats to live in
	 * different folders, or on CDNs, which often has completely different filenames for each file.
	 *
	 * Priority is determined by the property order (first property is tried first).  This is supported by both internal loading
	 * and loading with PreloadJS.
	 *
	 * <em>Note: an id is required for playback.</em>
	 *
	 * <h4>Example</h4>
	 *
	 *		var sounds = {path:"./audioPath/",
	 * 				manifest: [
	 *				{id: "cool", src: {mp3:"mp3/awesome.mp3", ogg:"noExtensionOggFile"}}
	 *		]};
	 *
	 *		createjs.Sound.alternateExtensions = ["mp3"];
	 *		createjs.Sound.addEventListener("fileload", handleLoad);
	 *		createjs.Sound.registerSounds(sounds);
	 *
	 * <h3>Known Browser and OS issues</h3>
	 * <b>IE 9 HTML Audio limitations</b><br />
	 * <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
	 * muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
	 * when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
     * <li>MP3 encoding will not always work for audio tags, particularly in Internet Explorer. We've found default
	 * encoding with 64kbps works.</li>
	 * <li>Occasionally very short samples will get cut off.</li>
	 * <li>There is a limit to how many audio tags you can load and play at once, which appears to be determined by
	 * hardware and browser settings.  See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe
	 * estimate.</li></ul>
	 *
	 * <b>Firefox 25 Web Audio limitations</b>
	 * <ul><li>mp3 audio files do not load properly on all windows machines, reported
	 * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>. </br>
	 * For this reason it is recommended to pass another FF supported type (ie ogg) first until this bug is resolved, if
	 * possible.</li></ul>

	 * <b>Safari limitations</b><br />
	 * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
	 *
	 * <b>iOS 6 Web Audio limitations</b><br />
	 * <ul><li>Sound is initially locked, and must be unlocked via a user-initiated event. Please see the section on
	 * Mobile Playback above.</li>
	 * <li>A bug exists that will distort un-cached web audio when a video element is present in the DOM that has audio
	 * at a different sampleRate.</li>
	 * </ul>
	 *
	 * <b>Android HTML Audio limitations</b><br />
	 * <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
	 * <li>We can only play audio inside a user event (touch/click).  This currently means you cannot loop sound or use
	 * a delay.</li></ul>
	 *
	 * <b>Web Audio and PreloadJS</b><br />
	 * <ul><li>Web Audio must be loaded through XHR, therefore when used with PreloadJS, tag loading is not possible.
	 * This means that tag loading can not be used to avoid cross domain issues.</li><ul>
	 *
	 * @class Sound
	 * @static
	 * @uses EventDispatcher
	 */
	function Sound() {
		throw "Sound cannot be instantiated";
	}

	var s = Sound;


// Static Properties
	/**
	 * The interrupt value to interrupt any currently playing instance with the same source, if the maximum number of
	 * instances of the sound are already playing.
	 * @property INTERRUPT_ANY
	 * @type {String}
	 * @default any
	 * @static
	 */
	s.INTERRUPT_ANY = "any";

	/**
	 * The interrupt value to interrupt the earliest currently playing instance with the same source that progressed the
	 * least distance in the audio track, if the maximum number of instances of the sound are already playing.
	 * @property INTERRUPT_EARLY
	 * @type {String}
	 * @default early
	 * @static
	 */
	s.INTERRUPT_EARLY = "early";

	/**
	 * The interrupt value to interrupt the currently playing instance with the same source that progressed the most
	 * distance in the audio track, if the maximum number of instances of the sound are already playing.
	 * @property INTERRUPT_LATE
	 * @type {String}
	 * @default late
	 * @static
	 */
	s.INTERRUPT_LATE = "late";

	/**
	 * The interrupt value to not interrupt any currently playing instances with the same source, if the maximum number of
	 * instances of the sound are already playing.
	 * @property INTERRUPT_NONE
	 * @type {String}
	 * @default none
	 * @static
	 */
	s.INTERRUPT_NONE = "none";

	/**
	 * Defines the playState of an instance that is still initializing.
	 * @property PLAY_INITED
	 * @type {String}
	 * @default playInited
	 * @static
	 */
	s.PLAY_INITED = "playInited";

	/**
	 * Defines the playState of an instance that is currently playing or paused.
	 * @property PLAY_SUCCEEDED
	 * @type {String}
	 * @default playSucceeded
	 * @static
	 */
	s.PLAY_SUCCEEDED = "playSucceeded";

	/**
	 * Defines the playState of an instance that was interrupted by another instance.
	 * @property PLAY_INTERRUPTED
	 * @type {String}
	 * @default playInterrupted
	 * @static
	 */
	s.PLAY_INTERRUPTED = "playInterrupted";

	/**
	 * Defines the playState of an instance that completed playback.
	 * @property PLAY_FINISHED
	 * @type {String}
	 * @default playFinished
	 * @static
	 */
	s.PLAY_FINISHED = "playFinished";

	/**
	 * Defines the playState of an instance that failed to play. This is usually caused by a lack of available channels
	 * when the interrupt mode was "INTERRUPT_NONE", the playback stalled, or the sound could not be found.
	 * @property PLAY_FAILED
	 * @type {String}
	 * @default playFailed
	 * @static
	 */
	s.PLAY_FAILED = "playFailed";

	/**
	 * A list of the default supported extensions that Sound will <i>try</i> to play. Plugins will check if the browser
	 * can play these types, so modifying this list before a plugin is initialized will allow the plugins to try to
	 * support additional media types.
	 *
	 * NOTE this does not currently work for {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
	 *
	 * More details on file formats can be found at <a href="http://en.wikipedia.org/wiki/Audio_file_format" target="_blank">http://en.wikipedia.org/wiki/Audio_file_format</a>.<br />
	 * A very detailed list of file formats can be found at <a href="http://www.fileinfo.com/filetypes/audio" target="_blank">http://www.fileinfo.com/filetypes/audio</a>.
	 * @property SUPPORTED_EXTENSIONS
	 * @type {Array[String]}
	 * @default ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"]
	 * @since 0.4.0
	 * @static
	 */
	s.SUPPORTED_EXTENSIONS = ["mp3", "ogg", "opus", "mpeg", "wav", "m4a", "mp4", "aiff", "wma", "mid"];

	/**
	 * Some extensions use another type of extension support to play (one of them is a codex).  This allows you to map
	 * that support so plugins can accurately determine if an extension is supported.  Adding to this list can help
	 * plugins determine more accurately if an extension is supported.
	 *
 	 * A useful list of extensions for each format can be found at <a href="http://html5doctor.com/html5-audio-the-state-of-play/" target="_blank">http://html5doctor.com/html5-audio-the-state-of-play/</a>.
	 * @property EXTENSION_MAP
	 * @type {Object}
	 * @since 0.4.0
	 * @default {m4a:"mp4"}
	 * @static
	 */
	s.EXTENSION_MAP = {
		m4a:"mp4"
	};

	/**
	 * The RegExp pattern used to parse file URIs. This supports simple file names, as well as full domain URIs with
	 * query strings. The resulting match is: protocol:$1 domain:$2 path:$3 file:$4 extension:$5 query:$6.
	 * @property FILE_PATTERN
	 * @type {RegExp}
	 * @static
	 * @protected
	 */
	s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;


// Class Public properties
	/**
	 * Determines the default behavior for interrupting other currently playing instances with the same source, if the
	 * maximum number of instances of the sound are already playing.  Currently the default is {{#crossLink "Sound/INTERRUPT_NONE:property"}}{{/crossLink}}
	 * but this can be set and will change playback behavior accordingly.  This is only used when {{#crossLink "Sound/play"}}{{/crossLink}}
	 * is called without passing a value for interrupt.
	 * @property defaultInterruptBehavior
	 * @type {String}
	 * @default Sound.INTERRUPT_NONE, or "none"
	 * @static
	 * @since 0.4.0
	 */
	s.defaultInterruptBehavior = s.INTERRUPT_NONE;  // OJR does s.INTERRUPT_ANY make more sense as default?  Needs game dev testing to see which case makes more sense.

	/**
	 * An array of extensions to attempt to use when loading sound, if the default is unsupported by the active plugin.
	 * These are applied in order, so if you try to Load Thunder.ogg in a browser that does not support ogg, and your
	 * extensions array is ["mp3", "m4a", "wav"] it will check mp3 support, then m4a, then wav. The audio files need
	 * to exist in the same location, as only the extension is altered.
	 *
	 * Note that regardless of which file is loaded, you can call {{#crossLink "Sound/createInstance"}}{{/crossLink}}
	 * and {{#crossLink "Sound/play"}}{{/crossLink}} using the same id or full source path passed for loading.
	 *
	 * <h4>Example</h4>
	 *
	 *	var sounds = [
	 *		{src:"myPath/mySound.ogg", id:"example"},
	 *	];
	 *	createjs.Sound.alternateExtensions = ["mp3"]; // now if ogg is not supported, SoundJS will try asset0.mp3
	 *	createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
	 *	createjs.Sound.registerSounds(sounds, assetPath);
	 *	// ...
	 *	createjs.Sound.play("myPath/mySound.ogg"); // works regardless of what extension is supported.  Note calling with ID is a better approach
	 *
	 * @property alternateExtensions
	 * @type {Array}
	 * @since 0.5.2
	 * @static
	 */
	s.alternateExtensions = [];

	/**
	 * The currently active plugin. If this is null, then no plugin could be initialized. If no plugin was specified,
	 * Sound attempts to apply the default plugins: {{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by
	 * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
	 * @property activePlugin
	 * @type {Object}
	 * @static
	 */
    s.activePlugin = null;


// class getter / setter properties
	/**
	 * Set the master volume of Sound. The master volume is multiplied against each sound's individual volume.  For
	 * example, if master volume is 0.5 and a sound's volume is 0.5, the resulting volume is 0.25. To set individual
	 * sound volume, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} instead.
	 *
	 * <h4>Example</h4>
	 *
	 *     createjs.Sound.volume = 0.5;
	 *
	 *
	 * @property volume
	 * @type {Number}
	 * @default 1
	 * @since 0.6.1
	 */
	s._masterVolume = 1;
	Object.defineProperty(s, "volume", {
		get: function () {return this._masterVolume;},
		set: function (value) {
				if (Number(value) == null) {return false;}
				value = Math.max(0, Math.min(1, value));
				s._masterVolume = value;
				if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
					var instances = this._instances;
					for (var i = 0, l = instances.length; i < l; i++) {
						instances[i].setMasterVolume(value);
					}
				}
			}
	});

	/**
	 * Mute/Unmute all audio. Note that muted audio still plays at 0 volume. This global mute value is maintained
	 * separately and when set will override, but not change the mute property of individual instances. To mute an individual
	 * instance, use AbstractSoundInstance {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} instead.
	 *
	 * <h4>Example</h4>
	 *
	 *     createjs.Sound.muted = true;
	 *
	 *
	 * @property muted
	 * @type {Boolean}
	 * @default false
	 * @since 0.6.1
	 */
	s._masterMute = false;
	// OJR references to the methods were not working, so the code had to be duplicated here
	Object.defineProperty(s, "muted", {
		get: function () {return this._masterMute;},
		set: function (value) {
				if (value == null) {return false;}

				this._masterMute = value;
				if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
					var instances = this._instances;
					for (var i = 0, l = instances.length; i < l; i++) {
						instances[i].setMasterMute(value);
					}
				}
				return true;
			}
	});

	/**
	 * Get the active plugins capabilities, which help determine if a plugin can be used in the current environment,
	 * or if the plugin supports a specific feature. Capabilities include:
	 * <ul>
	 *     <li><b>panning:</b> If the plugin can pan audio from left to right</li>
	 *     <li><b>volume;</b> If the plugin can control audio volume.</li>
	 *     <li><b>tracks:</b> The maximum number of audio tracks that can be played back at a time. This will be -1
	 *     if there is no known limit.</li>
	 * <br />An entry for each file type in {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}:
	 *     <li><b>mp3:</b> If MP3 audio is supported.</li>
	 *     <li><b>ogg:</b> If OGG audio is supported.</li>
	 *     <li><b>wav:</b> If WAV audio is supported.</li>
	 *     <li><b>mpeg:</b> If MPEG audio is supported.</li>
	 *     <li><b>m4a:</b> If M4A audio is supported.</li>
	 *     <li><b>mp4:</b> If MP4 audio is supported.</li>
	 *     <li><b>aiff:</b> If aiff audio is supported.</li>
	 *     <li><b>wma:</b> If wma audio is supported.</li>
	 *     <li><b>mid:</b> If mid audio is supported.</li>
	 * </ul>
	 *
	 * You can get a specific capability of the active plugin using standard object notation
	 *
	 * <h4>Example</h4>
	 *
	 *      var mp3 = createjs.Sound.capabilities.mp3;
	 *
	 * Note this property is read only.
	 *
	 * @property capabilities
	 * @type {Object}
	 * @static
	 * @readOnly
	 * @since 0.6.1
	 */
	Object.defineProperty(s, "capabilities", {
		get: function () {
					if (s.activePlugin == null) {return null;}
					return s.activePlugin._capabilities;
				},
		set: function (value) { return false;}
	});


// Class Private properties
	/**
	 * Determines if the plugins have been registered. If false, the first call to play() will instantiate the default
	 * plugins ({{#crossLink "WebAudioPlugin"}}{{/crossLink}}, followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}).
	 * If plugins have been registered, but none are applicable, then sound playback will fail.
	 * @property _pluginsRegistered
	 * @type {Boolean}
	 * @default false
	 * @static
	 * @protected
	 */
	s._pluginsRegistered = false;

	/**
	 * Used internally to assign unique IDs to each AbstractSoundInstance.
	 * @property _lastID
	 * @type {Number}
	 * @static
	 * @protected
	 */
	s._lastID = 0;

	/**
	 * An array containing all currently playing instances. This allows Sound to control the volume, mute, and playback of
	 * all instances when using static APIs like {{#crossLink "Sound/stop"}}{{/crossLink}} and {{#crossLink "Sound/setVolume"}}{{/crossLink}}.
	 * When an instance has finished playback, it gets removed via the {{#crossLink "Sound/finishedPlaying"}}{{/crossLink}}
	 * method. If the user replays an instance, it gets added back in via the {{#crossLink "Sound/_beginPlaying"}}{{/crossLink}}
	 * method.
	 * @property _instances
	 * @type {Array}
	 * @protected
	 * @static
	 */
	s._instances = [];

	/**
	 * An object hash storing objects with sound sources, startTime, and duration via there corresponding ID.
	 * @property _idHash
	 * @type {Object}
	 * @protected
	 * @static
	 */
	s._idHash = {};

	/**
	 * An object hash that stores preloading sound sources via the parsed source that is passed to the plugin.  Contains the
	 * source, id, and data that was passed in by the user.  Parsed sources can contain multiple instances of source, id,
	 * and data.
	 * @property _preloadHash
	 * @type {Object}
	 * @protected
	 * @static
	 */
	s._preloadHash = {};

	/**
	 * An object hash storing {{#crossLink "PlayPropsConfig"}}{{/crossLink}} via the parsed source that is passed as defaultPlayProps in
	 * {{#crossLink "Sound/registerSound"}}{{/crossLink}} and {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
	 * @property _defaultPlayPropsHash
	 * @type {Object}
	 * @protected
	 * @static
	 * @since 0.6.1
	 */
	s._defaultPlayPropsHash = {};


// EventDispatcher methods:
	s.addEventListener = null;
	s.removeEventListener = null;
	s.removeAllEventListeners = null;
	s.dispatchEvent = null;
	s.hasEventListener = null;
	s._listeners = null;

	createjs.EventDispatcher.initialize(s); // inject EventDispatcher methods.


// Events
	/**
	 * This event is fired when a file finishes loading internally. This event is fired for each loaded sound,
	 * so any handler methods should look up the <code>event.src</code> to handle a particular sound.
	 * @event fileload
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {String} src The source of the sound that was loaded.
	 * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
	 * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
	 * @since 0.4.1
	 */

	/**
	 * This event is fired when a file fails loading internally. This event is fired for each loaded sound,
	 * so any handler methods should look up the <code>event.src</code> to handle a particular sound.
	 * @event fileerror
	 * @param {Object} target The object that dispatched the event.
	 * @param {String} type The event type.
	 * @param {String} src The source of the sound that was loaded.
	 * @param {String} [id] The id passed in when the sound was registered. If one was not provided, it will be null.
	 * @param {Number|Object} [data] Any additional data associated with the item. If not provided, it will be undefined.
	 * @since 0.6.0
	 */


// Class Public Methods
	/**
	 * Get the preload rules to allow Sound to be used as a plugin by <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
	 * Any load calls that have the matching type or extension will fire the callback method, and use the resulting
	 * object, which is potentially modified by Sound. This helps when determining the correct path, as well as
	 * registering the audio instance(s) with Sound. This method should not be called, except by PreloadJS.
	 * @method getPreloadHandlers
	 * @return {Object} An object containing:
	 * <ul><li>callback: A preload callback that is fired when a file is added to PreloadJS, which provides
	 *      Sound a mechanism to modify the load parameters, select the correct file format, register the sound, etc.</li>
	 *      <li>types: A list of file types that are supported by Sound (currently supports "sound").</li>
	 *      <li>extensions: A list of file extensions that are supported by Sound (see {{#crossLink "Sound/SUPPORTED_EXTENSIONS:property"}}{{/crossLink}}).</li></ul>
	 * @static
	 * @protected
	 */
	s.getPreloadHandlers = function () {
		return {
			callback:createjs.proxy(s.initLoad, s),
			types:["sound"],
			extensions:s.SUPPORTED_EXTENSIONS
		};
	};

	/**
	 * Used to dispatch fileload events from internal loading.
	 * @method _handleLoadComplete
	 * @param event A loader event.
	 * @protected
	 * @static
	 * @since 0.6.0
	 */
	s._handleLoadComplete = function(event) {
		var src = event.target.getItem().src;
		if (!s._preloadHash[src]) {return;}

		for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
			var item = s._preloadHash[src][i];
			s._preloadHash[src][i] = true;

			if (!s.hasEventListener("fileload")) { continue; }

			var event = new createjs.Event("fileload");
			event.src = item.src;
			event.id = item.id;
			event.data = item.data;
			event.sprite = item.sprite;

			s.dispatchEvent(event);
		}
	};

	/**
	 * Used to dispatch error events from internal preloading.
	 * @param event
	 * @protected
	 * @since 0.6.0
	 * @static
	 */
	s._handleLoadError = function(event) {
		var src = event.target.getItem().src;
		if (!s._preloadHash[src]) {return;}

		for (var i = 0, l = s._preloadHash[src].length; i < l; i++) {
			var item = s._preloadHash[src][i];
			s._preloadHash[src][i] = false;

			if (!s.hasEventListener("fileerror")) { continue; }

			var event = new createjs.Event("fileerror");
			event.src = item.src;
			event.id = item.id;
			event.data = item.data;
			event.sprite = item.sprite;

			s.dispatchEvent(event);
		}
	};

	/**
	 * Used by {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} to register a Sound plugin.
	 *
	 * @method _registerPlugin
	 * @param {Object} plugin The plugin class to install.
	 * @return {Boolean} Whether the plugin was successfully initialized.
	 * @static
	 * @private
	 */
	s._registerPlugin = function (plugin) {
		// Note: Each plugin is passed in as a class reference, but we store the activePlugin as an instance
		if (plugin.isSupported()) {
			s.activePlugin = new plugin();
			return true;
		}
		return false;
	};

	/**
	 * Register a list of Sound plugins, in order of precedence. To register a single plugin, pass a single element in the array.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
	 *      createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
	 *
	 * @method registerPlugins
	 * @param {Array} plugins An array of plugins classes to install.
	 * @return {Boolean} Whether a plugin was successfully initialized.
	 * @static
	 */
	s.registerPlugins = function (plugins) {
		s._pluginsRegistered = true;
		for (var i = 0, l = plugins.length; i < l; i++) {
			if (s._registerPlugin(plugins[i])) {
				return true;
			}
		}
		return false;
	};

	/**
	 * Initialize the default plugins. This method is automatically called when any audio is played or registered before
	 * the user has manually registered plugins, and enables Sound to work without manual plugin setup. Currently, the
	 * default plugins are {{#crossLink "WebAudioPlugin"}}{{/crossLink}} followed by {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 *
	 * 	if (!createjs.initializeDefaultPlugins()) { return; }
	 *
	 * @method initializeDefaultPlugins
	 * @returns {Boolean} True if a plugin was initialized, false otherwise.
	 * @since 0.4.0
	 * @static
	 */
	s.initializeDefaultPlugins = function () {
		if (s.activePlugin != null) {return true;}
		if (s._pluginsRegistered) {return false;}
		if (s.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin])) {return true;}
		return false;
	};

	/**
	 * Determines if Sound has been initialized, and a plugin has been activated.
	 *
	 * <h4>Example</h4>
	 * This example sets up a Flash fallback, but only if there is no plugin specified yet.
	 *
	 * 	if (!createjs.Sound.isReady()) {
	 *		createjs.FlashAudioPlugin.swfPath = "../src/soundjs/flashaudio/";
	 * 		createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.HTMLAudioPlugin, createjs.FlashAudioPlugin]);
	 *	}
	 *
	 * @method isReady
	 * @return {Boolean} If Sound has initialized a plugin.
	 * @static
	 */
	s.isReady = function () {
		return (s.activePlugin != null);
	};

	/**
	 * Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
	 *
	 * @method getCapabilities
	 * @return {Object} An object containing the capabilities of the active plugin.
	 * @static
	 * @deprecated
	 */
	s.getCapabilities = function () {
		if (s.activePlugin == null) {return null;}
		return s.activePlugin._capabilities;
	};

	/**
	 * Deprecated, please use {{#crossLink "Sound/capabilities:property"}}{{/crossLink}} instead.
	 *
	 * @method getCapability
	 * @param {String} key The capability to retrieve
	 * @return {Number|Boolean} The value of the capability.
	 * @static
	 * @see getCapabilities
	 * @deprecated
	 */
	s.getCapability = function (key) {
		if (s.activePlugin == null) {return null;}
		return s.activePlugin._capabilities[key];
	};

	/**
	 * Process manifest items from <a href="http://preloadjs.com" target="_blank">PreloadJS</a>. This method is intended
	 * for usage by a plugin, and not for direct interaction.
	 * @method initLoad
	 * @param {Object} src The object to load.
	 * @return {Object|AbstractLoader} An instance of AbstractLoader.
	 * @protected
	 * @static
	 */
	s.initLoad = function (loadItem) {
		return s._registerSound(loadItem);
	};

	/**
	 * Internal method for loading sounds.  This should not be called directly.
	 *
	 * @method _registerSound
	 * @param {Object} src The object to load, containing src property and optionally containing id and data.
	 * @return {Object} An object with the modified values that were passed in, which defines the sound.
	 * Returns false if the source cannot be parsed or no plugins can be initialized.
	 * Returns true if the source is already loaded.
	 * @static
	 * @private
	 * @since 0.6.0
	 */

	s._registerSound = function (loadItem) {
		if (!s.initializeDefaultPlugins()) {return false;}

		var details;
		if (loadItem.src instanceof Object) {
			details = s._parseSrc(loadItem.src);
			details.src = loadItem.path + details.src;
		} else {
			details = s._parsePath(loadItem.src);
		}
		if (details == null) {return false;}
		loadItem.src = details.src;
		loadItem.type = "sound";

		var data = loadItem.data;
		var numChannels = null;
		if (data != null) {
			if (!isNaN(data.channels)) {
				numChannels = parseInt(data.channels);
			} else if (!isNaN(data)) {
				numChannels = parseInt(data);
			}

			if(data.audioSprite) {
				var sp;
				for(var i = data.audioSprite.length; i--; ) {
					sp = data.audioSprite[i];
					s._idHash[sp.id] = {src: loadItem.src, startTime: parseInt(sp.startTime), duration: parseInt(sp.duration)};

					if (sp.defaultPlayProps) {
						s._defaultPlayPropsHash[sp.id] = createjs.PlayPropsConfig.create(sp.defaultPlayProps);
					}
				}
			}
		}
		if (loadItem.id != null) {s._idHash[loadItem.id] = {src: loadItem.src}};
		var loader = s.activePlugin.register(loadItem);

		SoundChannel.create(loadItem.src, numChannels);

		// return the number of instances to the user.  This will also be returned in the load event.
		if (data == null || !isNaN(data)) {
			loadItem.data = numChannels || SoundChannel.maxPerChannel();
		} else {
			loadItem.data.channels = numChannels || SoundChannel.maxPerChannel();
		}

		if (loader.type) {loadItem.type = loader.type;}

		if (loadItem.defaultPlayProps) {
			s._defaultPlayPropsHash[loadItem.src] = createjs.PlayPropsConfig.create(loadItem.defaultPlayProps);
		}
		return loader;
	};

	/**
	 * Register an audio file for loading and future playback in Sound. This is automatically called when using
	 * <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.  It is recommended to register all sounds that
	 * need to be played back in order to properly prepare and preload them. Sound does internal preloading when required.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.Sound.alternateExtensions = ["mp3"];
	 *      createjs.Sound.on("fileload", handleLoad); // add an event listener for when load is completed
	 *      createjs.Sound.registerSound("myAudioPath/mySound.ogg", "myID", 3);
	 *      createjs.Sound.registerSound({ogg:"path1/mySound.ogg", mp3:"path2/mySoundNoExtension"}, "myID", 3);
	 *
	 *
	 * @method registerSound
	 * @param {String | Object} src The source or an Object with a "src" property or an Object with multiple extension labeled src properties.
	 * @param {String} [id] An id specified by the user to play the sound later.  Note id is required for when src is multiple extension labeled src properties.
	 * @param {Number | Object} [data] Data associated with the item. Sound uses the data parameter as the number of
	 * channels for an audio instance, however a "channels" property can be appended to the data object if it is used
	 * for other information. The audio channels will set a default based on plugin if no value is found.
	 * Sound also uses the data property to hold an {{#crossLink "AudioSprite"}}{{/crossLink}} array of objects in the following format {id, startTime, duration}.<br/>
	 *   id used to play the sound later, in the same manner as a sound src with an id.<br/>
	 *   startTime is the initial offset to start playback and loop from, in milliseconds.<br/>
	 *   duration is the amount of time to play the clip for, in milliseconds.<br/>
	 * This allows Sound to support audio sprites that are played back by id.
	 * @param {string} basePath Set a path that will be prepended to src for loading.
	 * @param {Object | PlayPropsConfig} defaultPlayProps Optional Playback properties that will be set as the defaults on any new AbstractSoundInstance.
	 * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for options.
	 * @return {Object} An object with the modified values that were passed in, which defines the sound.
	 * Returns false if the source cannot be parsed or no plugins can be initialized.
	 * Returns true if the source is already loaded.
	 * @static
	 * @since 0.4.0
	 */
	s.registerSound = function (src, id, data, basePath, defaultPlayProps) {
		var loadItem = {src: src, id: id, data:data, defaultPlayProps:defaultPlayProps};
		if (src instanceof Object && src.src) {
			basePath = id;
			loadItem = src;
		}
		loadItem = createjs.LoadItem.create(loadItem);
		loadItem.path = basePath;

		if (basePath != null && !(loadItem.src instanceof Object)) {loadItem.src = basePath + src;}

		var loader = s._registerSound(loadItem);
		if(!loader) {return false;}

		if (!s._preloadHash[loadItem.src]) { s._preloadHash[loadItem.src] = [];}
		s._preloadHash[loadItem.src].push(loadItem);
		if (s._preloadHash[loadItem.src].length == 1) {
			// OJR note this will disallow reloading a sound if loading fails or the source changes
			loader.on("complete", createjs.proxy(this._handleLoadComplete, this));
			loader.on("error", createjs.proxy(this._handleLoadError, this));
			s.activePlugin.preload(loader);
		} else {
			if (s._preloadHash[loadItem.src][0] == true) {return true;}
		}

		return loadItem;
	};

	/**
	 * Register an array of audio files for loading and future playback in Sound. It is recommended to register all
	 * sounds that need to be played back in order to properly prepare and preload them. Sound does internal preloading
	 * when required.
	 *
	 * <h4>Example</h4>
	 *
	 * 		var assetPath = "./myAudioPath/";
	 *      var sounds = [
	 *          {src:"asset0.ogg", id:"example"},
	 *          {src:"asset1.ogg", id:"1", data:6},
	 *          {src:"asset2.mp3", id:"works"}
	 *          {src:{mp3:"path1/asset3.mp3", ogg:"path2/asset3NoExtension}, id:"better"}
	 *      ];
	 *      createjs.Sound.alternateExtensions = ["mp3"];	// if the passed extension is not supported, try this extension
	 *      createjs.Sound.on("fileload", handleLoad); // call handleLoad when each sound loads
	 *      createjs.Sound.registerSounds(sounds, assetPath);
	 *
	 * @method registerSounds
	 * @param {Array} sounds An array of objects to load. Objects are expected to be in the format needed for
	 * {{#crossLink "Sound/registerSound"}}{{/crossLink}}: <code>{src:srcURI, id:ID, data:Data}</code>
	 * with "id" and "data" being optional.
	 * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to load.
	 * Note id is required if src is an object with extension labeled src properties.
	 * @param {string} basePath Set a path that will be prepended to each src when loading.  When creating, playing, or removing
	 * audio that was loaded with a basePath by src, the basePath must be included.
	 * @return {Object} An array of objects with the modified values that were passed in, which defines each sound.
	 * Like registerSound, it will return false for any values when the source cannot be parsed or if no plugins can be initialized.
	 * Also, it will return true for any values when the source is already loaded.
	 * @static
	 * @since 0.6.0
	 */
	s.registerSounds = function (sounds, basePath) {
		var returnValues = [];
		if (sounds.path) {
			if (!basePath) {
				basePath = sounds.path;
			} else {
				basePath = basePath + sounds.path;
			}
			sounds = sounds.manifest;
			// TODO document this feature
		}
		for (var i = 0, l = sounds.length; i < l; i++) {
			returnValues[i] = createjs.Sound.registerSound(sounds[i].src, sounds[i].id, sounds[i].data, basePath, sounds[i].defaultPlayProps);
		}
		return returnValues;
	};

	/**
	 * Remove a sound that has been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
	 * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
	 * <br />Note this will stop playback on active instances playing this sound before deleting them.
	 * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.Sound.removeSound("myID");
	 *      createjs.Sound.removeSound("myAudioBasePath/mySound.ogg");
	 *      createjs.Sound.removeSound("myPath/myOtherSound.mp3", "myBasePath/");
	 *      createjs.Sound.removeSound({mp3:"musicNoExtension", ogg:"music.ogg"}, "myBasePath/");
	 *
	 * @method removeSound
	 * @param {String | Object} src The src or ID of the audio, or an Object with a "src" property, or an Object with multiple extension labeled src properties.
	 * @param {string} basePath Set a path that will be prepended to each src when removing.
	 * @return {Boolean} True if sound is successfully removed.
	 * @static
	 * @since 0.4.1
	 */
	s.removeSound = function(src, basePath) {
		if (s.activePlugin == null) {return false;}

		if (src instanceof Object && src.src) {src = src.src;}

		var details;
		if (src instanceof Object) {
			details = s._parseSrc(src);
		} else {
			src = s._getSrcById(src).src;
			details = s._parsePath(src);
		}
		if (details == null) {return false;}
		src = details.src;
		if (basePath != null) {src = basePath + src;}

		for(var prop in s._idHash){
			if(s._idHash[prop].src == src) {
				delete(s._idHash[prop]);
			}
		}

		// clear from SoundChannel, which also stops and deletes all instances
		SoundChannel.removeSrc(src);

		delete(s._preloadHash[src]);

		s.activePlugin.removeSound(src);

		return true;
	};

	/**
	 * Remove an array of audio files that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
	 * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
	 * <br />Note this will stop playback on active instances playing this audio before deleting them.
	 * <br />Note if you passed in a basePath, you need to pass it or prepend it to the src here.
	 *
	 * <h4>Example</h4>
	 *
	 * 		assetPath = "./myPath/";
	 *      var sounds = [
	 *          {src:"asset0.ogg", id:"example"},
	 *          {src:"asset1.ogg", id:"1", data:6},
	 *          {src:"asset2.mp3", id:"works"}
	 *      ];
	 *      createjs.Sound.removeSounds(sounds, assetPath);
	 *
	 * @method removeSounds
	 * @param {Array} sounds An array of objects to remove. Objects are expected to be in the format needed for
	 * {{#crossLink "Sound/removeSound"}}{{/crossLink}}: <code>{srcOrID:srcURIorID}</code>.
	 * You can also pass an object with path and manifest properties, where path is a basePath and manifest is an array of objects to remove.
	 * @param {string} basePath Set a path that will be prepended to each src when removing.
	 * @return {Object} An array of Boolean values representing if the sounds with the same array index were
	 * successfully removed.
	 * @static
	 * @since 0.4.1
	 */
	s.removeSounds = function (sounds, basePath) {
		var returnValues = [];
		if (sounds.path) {
			if (!basePath) {
				basePath = sounds.path;
			} else {
				basePath = basePath + sounds.path;
			}
			sounds = sounds.manifest;
		}
		for (var i = 0, l = sounds.length; i < l; i++) {
			returnValues[i] = createjs.Sound.removeSound(sounds[i].src, basePath);
		}
		return returnValues;
	};

	/**
	 * Remove all sounds that have been registered with {{#crossLink "Sound/registerSound"}}{{/crossLink}} or
	 * {{#crossLink "Sound/registerSounds"}}{{/crossLink}}.
	 * <br />Note this will stop playback on all active sound instances before deleting them.
	 *
	 * <h4>Example</h4>
	 *
	 *     createjs.Sound.removeAllSounds();
	 *
	 * @method removeAllSounds
	 * @static
	 * @since 0.4.1
	 */
	s.removeAllSounds = function() {
		s._idHash = {};
		s._preloadHash = {};
		SoundChannel.removeAll();
		if (s.activePlugin) {s.activePlugin.removeAllSounds();}
	};

	/**
	 * Check if a source has been loaded by internal preloaders. This is necessary to ensure that sounds that are
	 * not completed preloading will not kick off a new internal preload if they are played.
	 *
	 * <h4>Example</h4>
	 *
	 *     var mySound = "assetPath/asset0.ogg";
	 *     if(createjs.Sound.loadComplete(mySound) {
	 *         createjs.Sound.play(mySound);
	 *     }
	 *
	 * @method loadComplete
	 * @param {String} src The src or id that is being loaded.
	 * @return {Boolean} If the src is already loaded.
	 * @since 0.4.0
	 * @static
	 */
	s.loadComplete = function (src) {
		if (!s.isReady()) { return false; }
		var details = s._parsePath(src);
		if (details) {
			src = s._getSrcById(details.src).src;
		} else {
			src = s._getSrcById(src).src;
		}
		if(s._preloadHash[src] == undefined) {return false;}
		return (s._preloadHash[src][0] == true);  // src only loads once, so if it's true for the first it's true for all
	};

	/**
	 * Parse the path of a sound. Alternate extensions will be attempted in order if the
	 * current extension is not supported
	 * @method _parsePath
	 * @param {String} value The path to an audio source.
	 * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
	 * and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
	 * @protected
	 * @static
	 */
	s._parsePath = function (value) {
		if (typeof(value) != "string") {value = value.toString();}

		var match = value.match(s.FILE_PATTERN);
		if (match == null) {return false;}

		var name = match[4];
		var ext = match[5];
		var c = s.capabilities;
		var i = 0;
		while (!c[ext]) {
			ext = s.alternateExtensions[i++];
			if (i > s.alternateExtensions.length) { return null;}	// no extensions are supported
		}
		value = value.replace("."+match[5], "."+ext);

		var ret = {name:name, src:value, extension:ext};
		return ret;
	};

	/**
	 * Parse the path of a sound based on properties of src matching with supported extensions.
	 * Returns false if none of the properties are supported
	 * @method _parseSrc
	 * @param {Object} value The paths to an audio source, indexed by extension type.
	 * @return {Object} A formatted object that can be registered with the {{#crossLink "Sound/activePlugin:property"}}{{/crossLink}}
	 * and returned to a preloader like <a href="http://preloadjs.com" target="_blank">PreloadJS</a>.
	 * @protected
	 * @static
	 */
	s._parseSrc = function (value) {
		var ret = {name:undefined, src:undefined, extension:undefined};
		var c = s.capabilities;

		for (var prop in value) {
		  if(value.hasOwnProperty(prop) && c[prop]) {
				ret.src = value[prop];
				ret.extension = prop;
				break;
		  }
		}
		if (!ret.src) {return false;}	// no matches

		var i = ret.src.lastIndexOf("/");
		if (i != -1) {
			ret.name = ret.src.slice(i+1);
		} else {
			ret.name = ret.src;
		}

		return ret;
	};

	/* ---------------
	 Static API.
	 --------------- */
	/**
	 * Play a sound and get a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to control. If the sound fails to play, a
	 * AbstractSoundInstance will still be returned, and have a playState of {{#crossLink "Sound/PLAY_FAILED:property"}}{{/crossLink}}.
	 * Note that even on sounds with failed playback, you may still be able to call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}},
	 * since the failure could be due to lack of available channels. If the src does not have a supported extension or
	 * if there is no available plugin, a default AbstractSoundInstance will be returned which will not play any audio, but will not generate errors.
	 *
	 * <h4>Example</h4>
	 *
	 *      createjs.Sound.on("fileload", handleLoad);
	 *      createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
	 *      function handleLoad(event) {
	 *      	createjs.Sound.play("myID");
	 *      	// store off AbstractSoundInstance for controlling
	 *      	var myInstance = createjs.Sound.play("myID", {interrupt: createjs.Sound.INTERRUPT_ANY, loop:-1});
	 *      }
	 *
	 * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
	 * This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
	 *
	 * <b>Parameters Deprecated</b><br />
	 * The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
	 *
	 * @method play
	 * @param {String} src The src or ID of the audio.
	 * @param {String | Object} [interrupt="none"|options] <b>This parameter will be renamed playProps in the next release.</b><br />
	 * This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
	 * including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
	 * <br /><strong>OR</strong><br />
	 * <b>Deprecated</b> How to interrupt any currently playing instances of audio with the same source,
	 * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
	 * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
	 * @param {Number} [delay=0] <b>Deprecated</b> The amount of time to delay the start of audio playback, in milliseconds.
	 * @param {Number} [offset=0] <b>Deprecated</b> The offset from the start of the audio to begin playback, in milliseconds.
	 * @param {Number} [loop=0] <b>Deprecated</b> How many times the audio loops when it reaches the end of playback. The default is 0 (no
	 * loops), and -1 can be used for infinite playback.
	 * @param {Number} [volume=1] <b>Deprecated</b> The volume of the sound, between 0 and 1. Note that the master volume is applied
	 * against the individual volume.
	 * @param {Number} [pan=0] <b>Deprecated</b> The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
	 * @param {Number} [startTime=null] <b>Deprecated</b> To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
	 * @param {Number} [duration=null] <b>Deprecated</b> To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
	 * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
	 * @static
	 */
	s.play = function (src, interrupt, delay, offset, loop, volume, pan, startTime, duration) {
		var playProps;
		if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
			playProps = createjs.PlayPropsConfig.create(interrupt);
		} else {
			playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan, startTime:startTime, duration:duration});
		}
		var instance = s.createInstance(src, playProps.startTime, playProps.duration);
		var ok = s._playInstance(instance, playProps);
		if (!ok) {instance._playFailed();}
		return instance;
	};

	/**
	 * Creates a {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} using the passed in src. If the src does not have a
	 * supported extension or if there is no available plugin, a default AbstractSoundInstance will be returned that can be
	 * called safely but does nothing.
	 *
	 * <h4>Example</h4>
	 *
	 *      var myInstance = null;
	 *      createjs.Sound.on("fileload", handleLoad);
	 *      createjs.Sound.registerSound("myAudioPath/mySound.mp3", "myID", 3);
	 *      function handleLoad(event) {
	 *      	myInstance = createjs.Sound.createInstance("myID");
	 *      	// alternately we could call the following
	 *      	myInstance = createjs.Sound.createInstance("myAudioPath/mySound.mp3");
	 *      }
	 *
	 * NOTE to create an audio sprite that has not already been registered, both startTime and duration need to be set.
	 * This is only when creating a new audio sprite, not when playing using the id of an already registered audio sprite.
	 *
	 * @method createInstance
	 * @param {String} src The src or ID of the audio.
	 * @param {Number} [startTime=null] To create an audio sprite (with duration), the initial offset to start playback and loop from, in milliseconds.
	 * @param {Number} [duration=null] To create an audio sprite (with startTime), the amount of time to play the clip for, in milliseconds.
	 * @return {AbstractSoundInstance} A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} that can be controlled after it is created.
	 * Unsupported extensions will return the default AbstractSoundInstance.
	 * @since 0.4.0
	 * @static
	 */
	s.createInstance = function (src, startTime, duration) {
		if (!s.initializeDefaultPlugins()) {return new createjs.DefaultSoundInstance(src, startTime, duration);}

		var defaultPlayProps = s._defaultPlayPropsHash[src];	// for audio sprites, which create and store defaults by id
		src = s._getSrcById(src);

		var details = s._parsePath(src.src);

		var instance = null;
		if (details != null && details.src != null) {
			SoundChannel.create(details.src);
			if (startTime == null) {startTime = src.startTime;}
			instance = s.activePlugin.create(details.src, startTime, duration || src.duration);

			defaultPlayProps = defaultPlayProps || s._defaultPlayPropsHash[details.src];
			if(defaultPlayProps) {
				instance.applyPlayProps(defaultPlayProps);
			}
		} else {
			instance = new createjs.DefaultSoundInstance(src, startTime, duration);
		}

		instance.uniqueId = s._lastID++;

		return instance;
	};

	/**
	 * Stop all audio (global stop). Stopped audio is reset, and not paused. To play audio that has been stopped,
	 * call AbstractSoundInstance {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 *
	 *     createjs.Sound.stop();
	 *
	 * @method stop
	 * @static
	 */
	s.stop = function () {
		var instances = this._instances;
		for (var i = instances.length; i--; ) {
			instances[i].stop();  // NOTE stop removes instance from this._instances
		}
	};

	/**
	 * Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
	 *
	 * @method setVolume
	 * @param {Number} value The master volume value. The acceptable range is 0-1.
	 * @static
	 * @deprecated
	 */
	s.setVolume = function (value) {
		if (Number(value) == null) {return false;}
		value = Math.max(0, Math.min(1, value));
		s._masterVolume = value;
		if (!this.activePlugin || !this.activePlugin.setVolume || !this.activePlugin.setVolume(value)) {
			var instances = this._instances;
			for (var i = 0, l = instances.length; i < l; i++) {
				instances[i].setMasterVolume(value);
			}
		}
	};

	/**
	 * Deprecated, please use {{#crossLink "Sound/volume:property"}}{{/crossLink}} instead.
	 *
	 * @method getVolume
	 * @return {Number} The master volume, in a range of 0-1.
	 * @static
	 * @deprecated
	 */
	s.getVolume = function () {
		return this._masterVolume;
	};

	/**
	 * Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
	 *
	 * @method setMute
	 * @param {Boolean} value Whether the audio should be muted or not.
	 * @return {Boolean} If the mute was set.
	 * @static
	 * @since 0.4.0
	 * @deprecated
	 */
	s.setMute = function (value) {
		if (value == null) {return false;}

		this._masterMute = value;
		if (!this.activePlugin || !this.activePlugin.setMute || !this.activePlugin.setMute(value)) {
			var instances = this._instances;
			for (var i = 0, l = instances.length; i < l; i++) {
				instances[i].setMasterMute(value);
			}
		}
		return true;
	};

	/**
	 * Deprecated, please use {{#crossLink "Sound/muted:property"}}{{/crossLink}} instead.
	 *
	 * @method getMute
	 * @return {Boolean} The mute value of Sound.
	 * @static
	 * @since 0.4.0
	 * @deprecated
	 */
	s.getMute = function () {
		return this._masterMute;
	};

	/**
	 * Set the default playback properties for all new SoundInstances of the passed in src or ID.
	 * See {{#crossLink "PlayPropsConfig"}}{{/crossLink}} for available properties.
	 *
	 * @method setDefaultPlayProps
	 * @param {String} src The src or ID used to register the audio.
	 * @param {Object | PlayPropsConfig} playProps The playback properties you would like to set.
	 * @since 0.6.1
	 */
	s.setDefaultPlayProps = function(src, playProps) {
		src = s._getSrcById(src);
		s._defaultPlayPropsHash[s._parsePath(src.src).src] = createjs.PlayPropsConfig.create(playProps);
	};

	/**
	 * Get the default playback properties for the passed in src or ID.  These properties are applied to all
	 * new SoundInstances.  Returns null if default does not exist.
	 *
	 * @method getDefaultPlayProps
	 * @param {String} src The src or ID used to register the audio.
	 * @returns {PlayPropsConfig} returns an existing PlayPropsConfig or null if one does not exist
	 * @since 0.6.1
	 */
	s.getDefaultPlayProps = function(src) {
		src = s._getSrcById(src);
		return s._defaultPlayPropsHash[s._parsePath(src.src).src];
	};


	/* ---------------
	 Internal methods
	 --------------- */
	/**
	 * Play an instance. This is called by the static API, as well as from plugins. This allows the core class to
	 * control delays.
	 * @method _playInstance
	 * @param {AbstractSoundInstance} instance The {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to start playing.
	 * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
	 * @return {Boolean} If the sound can start playing. Sounds that fail immediately will return false. Sounds that
	 * have a delay will return true, but may still fail to play.
	 * @protected
	 * @static
	 */
	s._playInstance = function (instance, playProps) {
		var defaultPlayProps = s._defaultPlayPropsHash[instance.src] || {};
		if (playProps.interrupt == null) {playProps.interrupt = defaultPlayProps.interrupt || s.defaultInterruptBehavior};
		if (playProps.delay == null) {playProps.delay = defaultPlayProps.delay || 0;}
		if (playProps.offset == null) {playProps.offset = instance.getPosition();}
		if (playProps.loop == null) {playProps.loop = instance.loop;}
		if (playProps.volume == null) {playProps.volume = instance.volume;}
		if (playProps.pan == null) {playProps.pan = instance.pan;}

		if (playProps.delay == 0) {
			var ok = s._beginPlaying(instance, playProps);
			if (!ok) {return false;}
		} else {
			//Note that we can't pass arguments to proxy OR setTimeout (IE only), so just wrap the function call.
			// OJR WebAudio may want to handle this differently, so it might make sense to move this functionality into the plugins in the future
			var delayTimeoutId = setTimeout(function () {
				s._beginPlaying(instance, playProps);
			}, playProps.delay);
			instance.delayTimeoutId = delayTimeoutId;
		}

		this._instances.push(instance);

		return true;
	};

	/**
	 * Begin playback. This is called immediately or after delay by {{#crossLink "Sound/playInstance"}}{{/crossLink}}.
	 * @method _beginPlaying
	 * @param {AbstractSoundInstance} instance A {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} to begin playback.
	 * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
	 * @return {Boolean} If the sound can start playing. If there are no available channels, or the instance fails to
	 * start, this will return false.
	 * @protected
	 * @static
	 */
	s._beginPlaying = function (instance, playProps) {
		if (!SoundChannel.add(instance, playProps.interrupt)) {
			return false;
		}
		var result = instance._beginPlaying(playProps);
		if (!result) {
			var index = createjs.indexOf(this._instances, instance);
			if (index > -1) {this._instances.splice(index, 1);}
			return false;
		}
		return true;
	};

	/**
	 * Get the source of a sound via the ID passed in with a register call. If no ID is found the value is returned
	 * instead.
	 * @method _getSrcById
	 * @param {String} value The ID the sound was registered with.
	 * @return {String} The source of the sound if it has been registered with this ID or the value that was passed in.
	 * @protected
	 * @static
	 */
	s._getSrcById = function (value) {
		return s._idHash[value] || {src: value};
	};

	/**
	 * A sound has completed playback, been interrupted, failed, or been stopped. This method removes the instance from
	 * Sound management. It will be added again, if the sound re-plays. Note that this method is called from the
	 * instances themselves.
	 * @method _playFinished
	 * @param {AbstractSoundInstance} instance The instance that finished playback.
	 * @protected
	 * @static
	 */
	s._playFinished = function (instance) {
		SoundChannel.remove(instance);
		var index = createjs.indexOf(this._instances, instance);
		if (index > -1) {this._instances.splice(index, 1);}	// OJR this will always be > -1, there is no way for an instance to exist without being added to this._instances
	};

	createjs.Sound = Sound;

	/**
	 * An internal class that manages the number of active {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} instances for
	 * each sound type. This method is only used internally by the {{#crossLink "Sound"}}{{/crossLink}} class.
	 *
	 * The number of sounds is artificially limited by Sound in order to prevent over-saturation of a
	 * single sound, as well as to stay within hardware limitations, although the latter may disappear with better
	 * browser support.
	 *
	 * When a sound is played, this class ensures that there is an available instance, or interrupts an appropriate
	 * sound that is already playing.
	 * #class SoundChannel
	 * @param {String} src The source of the instances
	 * @param {Number} [max=1] The number of instances allowed
	 * @constructor
	 * @protected
	 */
	function SoundChannel(src, max) {
		this.init(src, max);
	}

	/* ------------
	 Static API
	 ------------ */
	/**
	 * A hash of channel instances indexed by source.
	 * #property channels
	 * @type {Object}
	 * @static
	 */
	SoundChannel.channels = {};

	/**
	 * Create a sound channel. Note that if the sound channel already exists, this will fail.
	 * #method create
	 * @param {String} src The source for the channel
	 * @param {Number} max The maximum amount this channel holds. The default is {{#crossLink "SoundChannel.maxDefault"}}{{/crossLink}}.
	 * @return {Boolean} If the channels were created.
	 * @static
	 */
	SoundChannel.create = function (src, max) {
		var channel = SoundChannel.get(src);
		if (channel == null) {
			SoundChannel.channels[src] = new SoundChannel(src, max);
			return true;
		}
		return false;
	};
	/**
	 * Delete a sound channel, stop and delete all related instances. Note that if the sound channel does not exist, this will fail.
	 * #method remove
	 * @param {String} src The source for the channel
	 * @return {Boolean} If the channels were deleted.
	 * @static
	 */
	SoundChannel.removeSrc = function (src) {
		var channel = SoundChannel.get(src);
		if (channel == null) {return false;}
		channel._removeAll();	// this stops and removes all active instances
		delete(SoundChannel.channels[src]);
		return true;
	};
	/**
	 * Delete all sound channels, stop and delete all related instances.
	 * #method removeAll
	 * @static
	 */
	SoundChannel.removeAll = function () {
		for(var channel in SoundChannel.channels) {
			SoundChannel.channels[channel]._removeAll();	// this stops and removes all active instances
		}
		SoundChannel.channels = {};
	};
	/**
	 * Add an instance to a sound channel.
	 * #method add
	 * @param {AbstractSoundInstance} instance The instance to add to the channel
	 * @param {String} interrupt The interrupt value to use. Please see the {{#crossLink "Sound/play"}}{{/crossLink}}
	 * for details on interrupt modes.
	 * @return {Boolean} The success of the method call. If the channel is full, it will return false.
	 * @static
	 */
	SoundChannel.add = function (instance, interrupt) {
		var channel = SoundChannel.get(instance.src);
		if (channel == null) {return false;}
		return channel._add(instance, interrupt);
	};
	/**
	 * Remove an instance from the channel.
	 * #method remove
	 * @param {AbstractSoundInstance} instance The instance to remove from the channel
	 * @return The success of the method call. If there is no channel, it will return false.
	 * @static
	 */
	SoundChannel.remove = function (instance) {
		var channel = SoundChannel.get(instance.src);
		if (channel == null) {return false;}
		channel._remove(instance);
		return true;
	};
	/**
	 * Get the maximum number of sounds you can have in a channel.
	 * #method maxPerChannel
	 * @return {Number} The maximum number of sounds you can have in a channel.
	 */
	SoundChannel.maxPerChannel = function () {
		return p.maxDefault;
	};
	/**
	 * Get a channel instance by its src.
	 * #method get
	 * @param {String} src The src to use to look up the channel
	 * @static
	 */
	SoundChannel.get = function (src) {
		return SoundChannel.channels[src];
	};

	var p = SoundChannel.prototype;
	p.constructor = SoundChannel;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


	/**
	 * The source of the channel.
	 * #property src
	 * @type {String}
	 */
	p.src = null;

	/**
	 * The maximum number of instances in this channel.  -1 indicates no limit
	 * #property max
	 * @type {Number}
	 */
	p.max = null;

	/**
	 * The default value to set for max, if it isn't passed in.  Also used if -1 is passed.
	 * #property maxDefault
	 * @type {Number}
	 * @default 100
	 * @since 0.4.0
	 */
	p.maxDefault = 100;

	/**
	 * The current number of active instances.
	 * #property length
	 * @type {Number}
	 */
	p.length = 0;

	/**
	 * Initialize the channel.
	 * #method init
	 * @param {String} src The source of the channel
	 * @param {Number} max The maximum number of instances in the channel
	 * @protected
	 */
	p.init = function (src, max) {
		this.src = src;
		this.max = max || this.maxDefault;
		if (this.max == -1) {this.max = this.maxDefault;}
		this._instances = [];
	};

	/**
	 * Get an instance by index.
	 * #method get
	 * @param {Number} index The index to return.
	 * @return {AbstractSoundInstance} The AbstractSoundInstance at a specific instance.
	 */
	p._get = function (index) {
		return this._instances[index];
	};

	/**
	 * Add a new instance to the channel.
	 * #method add
	 * @param {AbstractSoundInstance} instance The instance to add.
	 * @return {Boolean} The success of the method call. If the channel is full, it will return false.
	 */
	p._add = function (instance, interrupt) {
		if (!this._getSlot(interrupt, instance)) {return false;}
		this._instances.push(instance);
		this.length++;
		return true;
	};

	/**
	 * Remove an instance from the channel, either when it has finished playing, or it has been interrupted.
	 * #method remove
	 * @param {AbstractSoundInstance} instance The instance to remove
	 * @return {Boolean} The success of the remove call. If the instance is not found in this channel, it will
	 * return false.
	 */
	p._remove = function (instance) {
		var index = createjs.indexOf(this._instances, instance);
		if (index == -1) {return false;}
		this._instances.splice(index, 1);
		this.length--;
		return true;
	};

	/**
	 * Stop playback and remove all instances from the channel.  Usually in response to a delete call.
	 * #method removeAll
	 */
	p._removeAll = function () {
		// Note that stop() removes the item from the list
		for (var i=this.length-1; i>=0; i--) {
			this._instances[i].stop();
		}
	};

	/**
	 * Get an available slot depending on interrupt value and if slots are available.
	 * #method getSlot
	 * @param {String} interrupt The interrupt value to use.
	 * @param {AbstractSoundInstance} instance The sound instance that will go in the channel if successful.
	 * @return {Boolean} Determines if there is an available slot. Depending on the interrupt mode, if there are no slots,
	 * an existing AbstractSoundInstance may be interrupted. If there are no slots, this method returns false.
	 */
	p._getSlot = function (interrupt, instance) {
		var target, replacement;

		if (interrupt != Sound.INTERRUPT_NONE) {
			// First replacement candidate
			replacement = this._get(0);
			if (replacement == null) {
				return true;
			}
		}

		for (var i = 0, l = this.max; i < l; i++) {
			target = this._get(i);

			// Available Space
			if (target == null) {
				return true;
			}

			// Audio is complete or not playing
			if (target.playState == Sound.PLAY_FINISHED ||
				target.playState == Sound.PLAY_INTERRUPTED ||
				target.playState == Sound.PLAY_FAILED) {
				replacement = target;
				break;
			}

			if (interrupt == Sound.INTERRUPT_NONE) {
				continue;
			}

			// Audio is a better candidate than the current target, according to playhead
			if ((interrupt == Sound.INTERRUPT_EARLY && target.getPosition() < replacement.getPosition()) ||
				(interrupt == Sound.INTERRUPT_LATE && target.getPosition() > replacement.getPosition())) {
					replacement = target;
			}
		}

		if (replacement != null) {
			replacement._interrupt();
			this._remove(replacement);
			return true;
		}
		return false;
	};

	p.toString = function () {
		return "[Sound SoundChannel]";
	};
	// do not add SoundChannel to namespace

}());

//##############################################################################
// AbstractSoundInstance.js
//##############################################################################

/**
 * A AbstractSoundInstance is created when any calls to the Sound API method {{#crossLink "Sound/play"}}{{/crossLink}} or
 * {{#crossLink "Sound/createInstance"}}{{/crossLink}} are made. The AbstractSoundInstance is returned by the active plugin
 * for control by the user.
 *
 * <h4>Example</h4>
 *
 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
 *
 * A number of additional parameters provide a quick way to determine how a sound is played. Please see the Sound
 * API method {{#crossLink "Sound/play"}}{{/crossLink}} for a list of arguments.
 *
 * Once a AbstractSoundInstance is created, a reference can be stored that can be used to control the audio directly through
 * the AbstractSoundInstance. If the reference is not stored, the AbstractSoundInstance will play out its audio (and any loops), and
 * is then de-referenced from the {{#crossLink "Sound"}}{{/crossLink}} class so that it can be cleaned up. If audio
 * playback has completed, a simple call to the {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}} instance method
 * will rebuild the references the Sound class need to control it.
 *
 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3", {loop:2});
 *      myInstance.on("loop", handleLoop);
 *      function handleLoop(event) {
 *          myInstance.volume = myInstance.volume * 0.5;
 *      }
 *
 * Events are dispatched from the instance to notify when the sound has completed, looped, or when playback fails
 *
 *      var myInstance = createjs.Sound.play("myAssetPath/mySrcFile.mp3");
 *      myInstance.on("complete", handleComplete);
 *      myInstance.on("loop", handleLoop);
 *      myInstance.on("failed", handleFailed);
 *
 *
 * @class AbstractSoundInstance
 * @param {String} src The path to and file name of the sound.
 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
 * @param {Object} playbackResource Any resource needed by plugin to support audio playback.
 * @extends EventDispatcher
 * @constructor
 */

(function () {
	"use strict";


// Constructor:
	var AbstractSoundInstance = function (src, startTime, duration, playbackResource) {
		this.EventDispatcher_constructor();


	// public properties:
		/**
		 * The source of the sound.
		 * @property src
		 * @type {String}
		 * @default null
		 */
		this.src = src;

		/**
		 * The unique ID of the instance. This is set by {{#crossLink "Sound"}}{{/crossLink}}.
		 * @property uniqueId
		 * @type {String} | Number
		 * @default -1
		 */
		this.uniqueId = -1;

		/**
		 * The play state of the sound. Play states are defined as constants on {{#crossLink "Sound"}}{{/crossLink}}.
		 * @property playState
		 * @type {String}
		 * @default null
		 */
		this.playState = null;

		/**
		 * A Timeout created by {{#crossLink "Sound"}}{{/crossLink}} when this AbstractSoundInstance is played with a delay.
		 * This allows AbstractSoundInstance to remove the delay if stop, pause, or cleanup are called before playback begins.
		 * @property delayTimeoutId
		 * @type {timeoutVariable}
		 * @default null
		 * @protected
		 * @since 0.4.0
		 */
		this.delayTimeoutId = null;
		// TODO consider moving delay into AbstractSoundInstance so it can be handled by plugins


	// private properties
	// Getter / Setter Properties
		// OJR TODO find original reason that we didn't use defined functions.  I think it was performance related
		/**
		 * The volume of the sound, between 0 and 1.
		 *
		 * The actual output volume of a sound can be calculated using:
		 * <code>myInstance.volume * createjs.Sound.getVolume();</code>
		 *
		 * @property volume
		 * @type {Number}
		 * @default 1
		 */
		this._volume =  1;
		Object.defineProperty(this, "volume", {
			get: this.getVolume,
			set: this.setVolume
		});

		/**
		 * The pan of the sound, between -1 (left) and 1 (right). Note that pan is not supported by HTML Audio.
		 *
		 * <br />Note in WebAudioPlugin this only gives us the "x" value of what is actually 3D audio.
		 *
		 * @property pan
		 * @type {Number}
		 * @default 0
		 */
		this._pan =  0;
		Object.defineProperty(this, "pan", {
			get: this.getPan,
			set: this.setPan
		});

		/**
		 * Audio sprite property used to determine the starting offset.
		 * @property startTime
		 * @type {Number}
		 * @default 0
		 * @since 0.6.1
		 */
		this._startTime = Math.max(0, startTime || 0);
		Object.defineProperty(this, "startTime", {
			get: this.getStartTime,
			set: this.setStartTime
		});

		/**
		 * Sets or gets the length of the audio clip, value is in milliseconds.
		 *
		 * @property duration
		 * @type {Number}
		 * @default 0
		 * @since 0.6.0
		 */
		this._duration = Math.max(0, duration || 0);
		Object.defineProperty(this, "duration", {
			get: this.getDuration,
			set: this.setDuration
		});

		/**
		 * Object that holds plugin specific resource need for audio playback.
		 * This is set internally by the plugin.  For example, WebAudioPlugin will set an array buffer,
		 * HTMLAudioPlugin will set a tag, FlashAudioPlugin will set a flash reference.
		 *
		 * @property playbackResource
		 * @type {Object}
		 * @default null
		 */
		this._playbackResource = null;
		Object.defineProperty(this, "playbackResource", {
			get: this.getPlaybackResource,
			set: this.setPlaybackResource
		});
		if(playbackResource !== false && playbackResource !== true) { this.setPlaybackResource(playbackResource); }

		/**
		 * The position of the playhead in milliseconds. This can be set while a sound is playing, paused, or stopped.
		 *
		 * @property position
		 * @type {Number}
		 * @default 0
		 * @since 0.6.0
		 */
		this._position = 0;
		Object.defineProperty(this, "position", {
			get: this.getPosition,
			set: this.setPosition
		});

		/**
		 * The number of play loops remaining. Negative values will loop infinitely.
		 *
		 * @property loop
		 * @type {Number}
		 * @default 0
		 * @public
		 * @since 0.6.0
		 */
		this._loop = 0;
		Object.defineProperty(this, "loop", {
			get: this.getLoop,
			set: this.setLoop
		});

		/**
		 * Mutes or unmutes the current audio instance.
		 *
		 * @property muted
		 * @type {Boolean}
		 * @default false
		 * @since 0.6.0
		 */
		this._muted = false;
		Object.defineProperty(this, "muted", {
			get: this.getMuted,
			set: this.setMuted
		});

		/**
		 * Pauses or resumes the current audio instance.
		 *
		 * @property paused
		 * @type {Boolean}
		 */
		this._paused = false;
		Object.defineProperty(this, "paused", {
			get: this.getPaused,
			set: this.setPaused
		});


	// Events
		/**
		 * The event that is fired when playback has started successfully.
		 * @event succeeded
		 * @param {Object} target The object that dispatched the event.
		 * @param {String} type The event type.
		 * @since 0.4.0
		 */

		/**
		 * The event that is fired when playback is interrupted. This happens when another sound with the same
		 * src property is played using an interrupt value that causes this instance to stop playing.
		 * @event interrupted
		 * @param {Object} target The object that dispatched the event.
		 * @param {String} type The event type.
		 * @since 0.4.0
		 */

		/**
		 * The event that is fired when playback has failed. This happens when there are too many channels with the same
		 * src property already playing (and the interrupt value doesn't cause an interrupt of another instance), or
		 * the sound could not be played, perhaps due to a 404 error.
		 * @event failed
		 * @param {Object} target The object that dispatched the event.
		 * @param {String} type The event type.
		 * @since 0.4.0
		 */

		/**
		 * The event that is fired when a sound has completed playing but has loops remaining.
		 * @event loop
		 * @param {Object} target The object that dispatched the event.
		 * @param {String} type The event type.
		 * @since 0.4.0
		 */

		/**
		 * The event that is fired when playback completes. This means that the sound has finished playing in its
		 * entirety, including its loop iterations.
		 * @event complete
		 * @param {Object} target The object that dispatched the event.
		 * @param {String} type The event type.
		 * @since 0.4.0
		 */
	};

	var p = createjs.extend(AbstractSoundInstance, createjs.EventDispatcher);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// Public Methods:
	/**
	 * Play an instance. This method is intended to be called on SoundInstances that already exist (created
	 * with the Sound API {{#crossLink "Sound/createInstance"}}{{/crossLink}} or {{#crossLink "Sound/play"}}{{/crossLink}}).
	 *
	 * <h4>Example</h4>
	 *
	 *      var myInstance = createjs.Sound.createInstance(mySrc);
	 *      myInstance.play({interrupt:createjs.Sound.INTERRUPT_ANY, loop:2, pan:0.5});
	 *
	 * Note that if this sound is already playing, this call will still set the passed in parameters.

	 * <b>Parameters Deprecated</b><br />
	 * The parameters for this method are deprecated in favor of a single parameter that is an Object or {{#crossLink "PlayPropsConfig"}}{{/crossLink}}.
	 *
	 * @method play
	 * @param {String | Object} [interrupt="none"|options] <b>This parameter will be renamed playProps in the next release.</b><br />
	 * This parameter can be an instance of {{#crossLink "PlayPropsConfig"}}{{/crossLink}} or an Object that contains any or all optional properties by name,
	 * including: interrupt, delay, offset, loop, volume, pan, startTime, and duration (see the above code sample).
	 * <br /><strong>OR</strong><br />
	 * <b>Deprecated</b> How to interrupt any currently playing instances of audio with the same source,
	 * if the maximum number of instances of the sound are already playing. Values are defined as <code>INTERRUPT_TYPE</code>
	 * constants on the Sound class, with the default defined by {{#crossLink "Sound/defaultInterruptBehavior:property"}}{{/crossLink}}.
	 * @param {Number} [delay=0] <b>Deprecated</b> The amount of time to delay the start of audio playback, in milliseconds.
	 * @param {Number} [offset=0] <b>Deprecated</b> The offset from the start of the audio to begin playback, in milliseconds.
	 * @param {Number} [loop=0] <b>Deprecated</b> How many times the audio loops when it reaches the end of playback. The default is 0 (no
	 * loops), and -1 can be used for infinite playback.
	 * @param {Number} [volume=1] <b>Deprecated</b> The volume of the sound, between 0 and 1. Note that the master volume is applied
	 * against the individual volume.
	 * @param {Number} [pan=0] <b>Deprecated</b> The left-right pan of the sound (if supported), between -1 (left) and 1 (right).
	 * Note that pan is not supported for HTML Audio.
	 * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
	 */
	p.play = function (interrupt, delay, offset, loop, volume, pan) {
		var playProps;
		if (interrupt instanceof Object || interrupt instanceof createjs.PlayPropsConfig) {
			playProps = createjs.PlayPropsConfig.create(interrupt);
		} else {
			playProps = createjs.PlayPropsConfig.create({interrupt:interrupt, delay:delay, offset:offset, loop:loop, volume:volume, pan:pan});
		}

		if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this.applyPlayProps(playProps);
			if (this._paused) {	this.setPaused(false); }
			return;
		}
		this._cleanUp();
		createjs.Sound._playInstance(this, playProps);	// make this an event dispatch??
		return this;
	};

	/**
	 * Stop playback of the instance. Stopped sounds will reset their position to 0, and calls to {{#crossLink "AbstractSoundInstance/resume"}}{{/crossLink}}
	 * will fail. To start playback again, call {{#crossLink "AbstractSoundInstance/play"}}{{/crossLink}}.
     *
     * If you don't want to lose your position use yourSoundInstance.paused = true instead. {{#crossLink "AbstractSoundInstance/paused"}}{{/crossLink}}.
	 *
	 * <h4>Example</h4>
	 *
	 *     myInstance.stop();
	 *
	 * @method stop
	 * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
	 */
	p.stop = function () {
		this._position = 0;
		this._paused = false;
		this._handleStop();
		this._cleanUp();
		this.playState = createjs.Sound.PLAY_FINISHED;
		return this;
	};

	/**
	 * Remove all external references and resources from AbstractSoundInstance.  Note this is irreversible and AbstractSoundInstance will no longer work
	 * @method destroy
	 * @since 0.6.0
	 */
	p.destroy = function() {
		this._cleanUp();
		this.src = null;
		this.playbackResource = null;

		this.removeAllEventListeners();
	};

	/**
	 * Takes an PlayPropsConfig or Object with the same properties and sets them on this instance.
	 * @method applyPlayProps
	 * @param {PlayPropsConfig | Object} playProps A PlayPropsConfig or object containing the same properties.
	 * @since 0.6.1
	 * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
	 */
	p.applyPlayProps = function(playProps) {
		if (playProps.offset != null) { this.setPosition(playProps.offset) }
		if (playProps.loop != null) { this.setLoop(playProps.loop); }
		if (playProps.volume != null) { this.setVolume(playProps.volume); }
		if (playProps.pan != null) { this.setPan(playProps.pan); }
		if (playProps.startTime != null) {
			this.setStartTime(playProps.startTime);
			this.setDuration(playProps.duration);
		}
		return this;
	};

	p.toString = function () {
		return "[AbstractSoundInstance]";
	};

// get/set methods that allow support for IE8
	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property,
	 *
	 * @deprecated
	 * @method getPaused
	 * @returns {boolean} If the instance is currently paused
	 * @since 0.6.0
	 */
	p.getPaused = function() {
		return this._paused;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/paused:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setPaused
	 * @param {boolean} value
	 * @since 0.6.0
	 * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
	 */
	p.setPaused = function (value) {
		if ((value !== true && value !== false) || this._paused == value) {return;}
		if (value == true && this.playState != createjs.Sound.PLAY_SUCCEEDED) {return;}
		this._paused = value;
		if(value) {
			this._pause();
		} else {
			this._resume();
		}
		clearTimeout(this.delayTimeoutId);
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setVolume
	 * @param {Number} value The volume to set, between 0 and 1.
	 * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
	 */
	p.setVolume = function (value) {
		if (value == this._volume) { return this; }
		this._volume = Math.max(0, Math.min(1, value));
		if (!this._muted) {
			this._updateVolume();
		}
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/volume:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getVolume
	 * @return {Number} The current volume of the sound instance.
	 */
	p.getVolume = function () {
		return this._volume;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setMuted
	 * @param {Boolean} value If the sound should be muted.
	 * @return {AbstractSoundInstance} A reference to itself, intended for chaining calls.
	 * @since 0.6.0
	 */
	p.setMuted = function (value) {
		if (value !== true && value !== false) {return;}
		this._muted = value;
		this._updateVolume();
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/muted:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getMuted
	 * @return {Boolean} If the sound is muted.
	 * @since 0.6.0
	 */
	p.getMuted = function () {
		return this._muted;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setPan
	 * @param {Number} value The pan value, between -1 (left) and 1 (right).
	 * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
	 */
	p.setPan = function (value) {
		if(value == this._pan) { return this; }
		this._pan = Math.max(-1, Math.min(1, value));
		this._updatePan();
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/pan:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getPan
	 * @return {Number} The value of the pan, between -1 (left) and 1 (right).
	 */
	p.getPan = function () {
		return this._pan;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getPosition
	 * @return {Number} The position of the playhead in the sound, in milliseconds.
	 */
	p.getPosition = function () {
		if (!this._paused && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this._position = this._calculateCurrentPosition();
		}
		return this._position;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/position:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setPosition
	 * @param {Number} value The position to place the playhead, in milliseconds.
	 * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
	 */
	p.setPosition = function (value) {
		this._position = Math.max(0, value);
		if (this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this._updatePosition();
		}
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getStartTime
	 * @return {Number} The startTime of the sound instance in milliseconds.
	 */
	p.getStartTime = function () {
		return this._startTime;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/startTime:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setStartTime
	 * @param {number} value The new startTime time in milli seconds.
	 * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
	 */
	p.setStartTime = function (value) {
		if (value == this._startTime) { return this; }
		this._startTime = Math.max(0, value || 0);
		this._updateStartTime();
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getDuration
	 * @return {Number} The duration of the sound instance in milliseconds.
	 */
	p.getDuration = function () {
		return this._duration;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/duration:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setDuration
	 * @param {number} value The new duration time in milli seconds.
	 * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
	 * @since 0.6.0
	 */
	p.setDuration = function (value) {
		if (value == this._duration) { return this; }
		this._duration = Math.max(0, value || 0);
		this._updateDuration();
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setPlayback
	 * @param {Object} value The new playback resource.
	 * @return {AbstractSoundInstance} Returns reference to itself for chaining calls
	 * @since 0.6.0
	 **/
	p.setPlaybackResource = function (value) {
		this._playbackResource = value;
		if (this._duration == 0) { this._setDurationFromSource(); }
		return this;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/playbackResource:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method setPlayback
	 * @param {Object} value The new playback resource.
	 * @return {Object} playback resource used for playing audio
	 * @since 0.6.0
	 **/
	p.getPlaybackResource = function () {
		return this._playbackResource;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property
	 *
	 * @deprecated
	 * @method getLoop
	 * @return {number}
	 * @since 0.6.0
	 **/
	p.getLoop = function () {
		return this._loop;
	};

	/**
	 * DEPRECATED, please use {{#crossLink "AbstractSoundInstance/loop:property"}}{{/crossLink}} directly as a property,
	 *
	 * @deprecated
	 * @method setLoop
	 * @param {number} value The number of times to loop after play.
	 * @since 0.6.0
	 */
	p.setLoop = function (value) {
		if(this._playbackResource != null) {
			// remove looping
			if (this._loop != 0 && value == 0) {
				this._removeLooping(value);
			}
			// add looping
			else if (this._loop == 0 && value != 0) {
				this._addLooping(value);
			}
		}
		this._loop = value;
	};


// Private Methods:
	/**
	 * A helper method that dispatches all events for AbstractSoundInstance.
	 * @method _sendEvent
	 * @param {String} type The event type
	 * @protected
	 */
	p._sendEvent = function (type) {
		var event = new createjs.Event(type);
		this.dispatchEvent(event);
	};

	/**
	 * Clean up the instance. Remove references and clean up any additional properties such as timers.
	 * @method _cleanUp
	 * @protected
	 */
	p._cleanUp = function () {
		clearTimeout(this.delayTimeoutId); // clear timeout that plays delayed sound
		this._handleCleanUp();
		this._paused = false;

		createjs.Sound._playFinished(this);	// TODO change to an event
	};

	/**
	 * The sound has been interrupted.
	 * @method _interrupt
	 * @protected
	 */
	p._interrupt = function () {
		this._cleanUp();
		this.playState = createjs.Sound.PLAY_INTERRUPTED;
		this._sendEvent("interrupted");
	};

	/**
	 * Called by the Sound class when the audio is ready to play (delay has completed). Starts sound playing if the
	 * src is loaded, otherwise playback will fail.
	 * @method _beginPlaying
	 * @param {PlayPropsConfig} playProps A PlayPropsConfig object.
	 * @return {Boolean} If playback succeeded.
	 * @protected
	 */
	// OJR FlashAudioSoundInstance overwrites
	p._beginPlaying = function (playProps) {
		this.setPosition(playProps.offset);
		this.setLoop(playProps.loop);
		this.setVolume(playProps.volume);
		this.setPan(playProps.pan);
		if (playProps.startTime != null) {
			this.setStartTime(playProps.startTime);
			this.setDuration(playProps.duration);
		}

		if (this._playbackResource != null && this._position < this._duration) {
			this._paused = false;
			this._handleSoundReady();
			this.playState = createjs.Sound.PLAY_SUCCEEDED;
			this._sendEvent("succeeded");
			return true;
		} else {
			this._playFailed();
			return false;
		}
	};

	/**
	 * Play has failed, which can happen for a variety of reasons.
	 * Cleans up instance and dispatches failed event
	 * @method _playFailed
	 * @private
	 */
	p._playFailed = function () {
		this._cleanUp();
		this.playState = createjs.Sound.PLAY_FAILED;
		this._sendEvent("failed");
	};

	/**
	 * Audio has finished playing. Manually loop it if required.
	 * @method _handleSoundComplete
	 * @param event
	 * @protected
	 */
	p._handleSoundComplete = function (event) {
		this._position = 0;  // have to set this as it can be set by pause during playback

		if (this._loop != 0) {
			this._loop--;  // NOTE this introduces a theoretical limit on loops = float max size x 2 - 1
			this._handleLoop();
			this._sendEvent("loop");
			return;
		}

		this._cleanUp();
		this.playState = createjs.Sound.PLAY_FINISHED;
		this._sendEvent("complete");
	};

// Plugin specific code
	/**
	 * Handles starting playback when the sound is ready for playing.
	 * @method _handleSoundReady
	 * @protected
 	 */
	p._handleSoundReady = function () {
		// plugin specific code
	};

	/**
	 * Internal function used to update the volume based on the instance volume, master volume, instance mute value,
	 * and master mute value.
	 * @method _updateVolume
	 * @protected
	 */
	p._updateVolume = function () {
		// plugin specific code
	};

	/**
	 * Internal function used to update the pan
	 * @method _updatePan
	 * @protected
	 * @since 0.6.0
	 */
	p._updatePan = function () {
		// plugin specific code
	};

	/**
	 * Internal function used to update the startTime of the audio.
	 * @method _updateStartTime
	 * @protected
	 * @since 0.6.1
	 */
	p._updateStartTime = function () {
		// plugin specific code
	};

	/**
	 * Internal function used to update the duration of the audio.
	 * @method _updateDuration
	 * @protected
	 * @since 0.6.0
	 */
	p._updateDuration = function () {
		// plugin specific code
	};

	/**
	 * Internal function used to get the duration of the audio from the source we'll be playing.
	 * @method _updateDuration
	 * @protected
	 * @since 0.6.0
	 */
	p._setDurationFromSource = function () {
		// plugin specific code
	};

	/**
	 * Internal function that calculates the current position of the playhead and sets this._position to that value
	 * @method _calculateCurrentPosition
	 * @protected
	 * @since 0.6.0
	 */
	p._calculateCurrentPosition = function () {
		// plugin specific code that sets this.position
	};

	/**
	 * Internal function used to update the position of the playhead.
	 * @method _updatePosition
	 * @protected
	 * @since 0.6.0
	 */
	p._updatePosition = function () {
		// plugin specific code
	};

	/**
	 * Internal function called when looping is removed during playback.
	 * @method _removeLooping
	 * @param {number} value The number of times to loop after play.
	 * @protected
	 * @since 0.6.0
	 */
	p._removeLooping = function (value) {
		// plugin specific code
	};

	/**
	 * Internal function called when looping is added during playback.
	 * @method _addLooping
	 * @param {number} value The number of times to loop after play.
	 * @protected
	 * @since 0.6.0
	 */
	p._addLooping = function (value) {
		// plugin specific code
	};

	/**
	 * Internal function called when pausing playback
	 * @method _pause
	 * @protected
	 * @since 0.6.0
	 */
	p._pause = function () {
		// plugin specific code
	};

	/**
	 * Internal function called when resuming playback
	 * @method _resume
	 * @protected
	 * @since 0.6.0
	 */
	p._resume = function () {
		// plugin specific code
	};

	/**
	 * Internal function called when stopping playback
	 * @method _handleStop
	 * @protected
	 * @since 0.6.0
	 */
	p._handleStop = function() {
		// plugin specific code
	};

	/**
	 * Internal function called when AbstractSoundInstance is being cleaned up
	 * @method _handleCleanUp
	 * @protected
	 * @since 0.6.0
	 */
	p._handleCleanUp = function() {
		// plugin specific code
	};

	/**
	 * Internal function called when AbstractSoundInstance has played to end and is looping
	 * @method _handleLoop
	 * @protected
	 * @since 0.6.0
	 */
	p._handleLoop = function () {
		// plugin specific code
	};

	createjs.AbstractSoundInstance = createjs.promote(AbstractSoundInstance, "EventDispatcher");
	createjs.DefaultSoundInstance = createjs.AbstractSoundInstance;	// used when no plugin is supported
}());

//##############################################################################
// AbstractPlugin.js
//##############################################################################

(function () {
	"use strict";


// constructor:
 	/**
	 * A default plugin class used as a base for all other plugins.
	 * @class AbstractPlugin
	 * @constructor
	 * @since 0.6.0
	 */

	var AbstractPlugin = function () {
	// private properties:
		/**
		 * The capabilities of the plugin.
		 * method and is used internally.
		 * @property _capabilities
		 * @type {Object}
		 * @default null
		 * @protected
		 * @static
		 */
		this._capabilities = null;

		/**
		 * Object hash indexed by the source URI of all created loaders, used to properly destroy them if sources are removed.
		 * @type {Object}
		 * @protected
		 */
		this._loaders = {};

		/**
		 * Object hash indexed by the source URI of each file to indicate if an audio source has begun loading,
		 * is currently loading, or has completed loading.  Can be used to store non boolean data after loading
		 * is complete (for example arrayBuffers for web audio).
		 * @property _audioSources
		 * @type {Object}
		 * @protected
		 */
		this._audioSources = {};

		/**
		 * Object hash indexed by the source URI of all created SoundInstances, updates the playbackResource if it loads after they are created,
		 * and properly destroy them if sources are removed
		 * @type {Object}
		 * @protected
		 */
		this._soundInstances = {};

		/**
		 * The internal master volume value of the plugin.
		 * @property _volume
		 * @type {Number}
		 * @default 1
		 * @protected
		 */
		this._volume = 1;

		/**
		 * A reference to a loader class used by a plugin that must be set.
		 * @type {Object}
		 * @protected
		 */
		this._loaderClass;

		/**
		 * A reference to an AbstractSoundInstance class used by a plugin that must be set.
		 * @type {Object}
		 * @protected;
		 */
		this._soundInstanceClass;
	};
	var p = AbstractPlugin.prototype;

	/**
	 * <strong>REMOVED</strong>. Removed in favor of using `MySuperClass_constructor`.
	 * See {{#crossLink "Utility Methods/extend"}}{{/crossLink}} and {{#crossLink "Utility Methods/promote"}}{{/crossLink}}
	 * for details.
	 *
	 * There is an inheritance tutorial distributed with EaselJS in /tutorials/Inheritance.
	 *
	 * @method initialize
	 * @protected
	 * @deprecated
	 */
	// p.initialize = function() {}; // searchable for devs wondering where it is.


// Static Properties:
// NOTE THESE PROPERTIES NEED TO BE ADDED TO EACH PLUGIN
	/**
	 * The capabilities of the plugin. This is generated via the _generateCapabilities method and is used internally.
	 * @property _capabilities
	 * @type {Object}
	 * @default null
	 * @protected
	 * @static
	 */
	AbstractPlugin._capabilities = null;

	/**
	 * Determine if the plugin can be used in the current browser/OS.
	 * @method isSupported
	 * @return {Boolean} If the plugin can be initialized.
	 * @static
	 */
	AbstractPlugin.isSupported = function () {
		return true;
	};


// public methods:
	/**
	 * Pre-register a sound for preloading and setup. This is called by {{#crossLink "Sound"}}{{/crossLink}}.
	 * Note all plugins provide a <code>Loader</code> instance, which <a href="http://preloadjs.com" target="_blank">PreloadJS</a>
	 * can use to assist with preloading.
	 * @method register
	 * @param {String} loadItem An Object containing the source of the audio
	 * Note that not every plugin will manage this value.
	 * @return {Object} A result object, containing a "tag" for preloading purposes.
	 */
	p.register = function (loadItem) {
		var loader = this._loaders[loadItem.src];
		if(loader && !loader.canceled) {return this._loaders[loadItem.src];}	// already loading/loaded this, so don't load twice
		// OJR potential issue that we won't be firing loaded event, might need to trigger if this is already loaded?
		this._audioSources[loadItem.src] = true;
		this._soundInstances[loadItem.src] = [];
		loader = new this._loaderClass(loadItem);
		loader.on("complete", this._handlePreloadComplete, this);
		this._loaders[loadItem.src] = loader;
		return loader;
	};

	// note sound calls register before calling preload
	/**
	 * Internally preload a sound.
	 * @method preload
	 * @param {Loader} loader The sound URI to load.
	 */
	p.preload = function (loader) {
		loader.on("error", this._handlePreloadError, this);
		loader.load();
	};

	/**
	 * Checks if preloading has started for a specific source. If the source is found, we can assume it is loading,
	 * or has already finished loading.
	 * @method isPreloadStarted
	 * @param {String} src The sound URI to check.
	 * @return {Boolean}
	 */
	p.isPreloadStarted = function (src) {
		return (this._audioSources[src] != null);
	};

	/**
	 * Checks if preloading has finished for a specific source.
	 * @method isPreloadComplete
	 * @param {String} src The sound URI to load.
	 * @return {Boolean}
	 */
	p.isPreloadComplete = function (src) {
		return (!(this._audioSources[src] == null || this._audioSources[src] == true));
	};

	/**
	 * Remove a sound added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
	 * @method removeSound
	 * @param {String} src The sound URI to unload.
	 */
	p.removeSound = function (src) {
		if (!this._soundInstances[src]) { return; }
		for (var i = this._soundInstances[src].length; i--; ) {
			var item = this._soundInstances[src][i];
			item.destroy();
		}
		delete(this._soundInstances[src]);
		delete(this._audioSources[src]);
		if(this._loaders[src]) { this._loaders[src].destroy(); }
		delete(this._loaders[src]);
	};

	/**
	 * Remove all sounds added using {{#crossLink "WebAudioPlugin/register"}}{{/crossLink}}. Note this does not cancel a preload.
	 * @method removeAllSounds
	 * @param {String} src The sound URI to unload.
	 */
	p.removeAllSounds = function () {
		for(var key in this._audioSources) {
			this.removeSound(key);
		}
	};

	/**
	 * Create a sound instance. If the sound has not been preloaded, it is internally preloaded here.
	 * @method create
	 * @param {String} src The sound source to use.
	 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
	 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
	 * @return {AbstractSoundInstance} A sound instance for playback and control.
	 */
	p.create = function (src, startTime, duration) {
		if (!this.isPreloadStarted(src)) {
			this.preload(this.register(src));
		}
		var si = new this._soundInstanceClass(src, startTime, duration, this._audioSources[src]);
		this._soundInstances[src].push(si);
		return si;
	};

	// if a plugin does not support volume and mute, it should set these to null
	/**
	 * Set the master volume of the plugin, which affects all SoundInstances.
	 * @method setVolume
	 * @param {Number} value The volume to set, between 0 and 1.
	 * @return {Boolean} If the plugin processes the setVolume call (true). The Sound class will affect all the
	 * instances manually otherwise.
	 */
	p.setVolume = function (value) {
		this._volume = value;
		this._updateVolume();
		return true;
	};

	/**
	 * Get the master volume of the plugin, which affects all SoundInstances.
	 * @method getVolume
	 * @return {Number} The volume level, between 0 and 1.
	 */
	p.getVolume = function () {
		return this._volume;
	};

	/**
	 * Mute all sounds via the plugin.
	 * @method setMute
	 * @param {Boolean} value If all sound should be muted or not. Note that plugin-level muting just looks up
	 * the mute value of Sound {{#crossLink "Sound/getMute"}}{{/crossLink}}, so this property is not used here.
	 * @return {Boolean} If the mute call succeeds.
	 */
	p.setMute = function (value) {
		this._updateVolume();
		return true;
	};

	// plugins should overwrite this method
	p.toString = function () {
		return "[AbstractPlugin]";
	};


// private methods:
	/**
	 * Handles internal preload completion.
	 * @method _handlePreloadComplete
	 * @protected
	 */
	p._handlePreloadComplete = function (event) {
		var src = event.target.getItem().src;
		this._audioSources[src] = event.result;
		for (var i = 0, l = this._soundInstances[src].length; i < l; i++) {
			var item = this._soundInstances[src][i];
			item.setPlaybackResource(this._audioSources[src]);
			// ToDo consider adding play call here if playstate == playfailed
		}
	};

	/**
	 * Handles internal preload erros
	 * @method _handlePreloadError
	 * @param event
	 * @protected
	 */
	p._handlePreloadError = function(event) {
		//delete(this._audioSources[src]);
	};

	/**
	 * Set the gain value for master audio. Should not be called externally.
	 * @method _updateVolume
	 * @protected
	 */
	p._updateVolume = function () {
		// Plugin Specific code
	};

	createjs.AbstractPlugin = AbstractPlugin;
}());

//##############################################################################
// WebAudioLoader.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * Loader provides a mechanism to preload Web Audio content via PreloadJS or internally. Instances are returned to
	 * the preloader, and the load method is called when the asset needs to be requested.
	 *
	 * @class WebAudioLoader
	 * @param {String} loadItem The item to be loaded
	 * @extends XHRRequest
	 * @protected
	 */
	function Loader(loadItem) {
		this.AbstractLoader_constructor(loadItem, true, createjs.AbstractLoader.SOUND);

	};
	var p = createjs.extend(Loader, createjs.AbstractLoader);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


	/**
	 * web audio context required for decoding audio
	 * @property context
	 * @type {AudioContext}
	 * @static
	 */
	Loader.context = null;


// public methods
	p.toString = function () {
		return "[WebAudioLoader]";
	};


// private methods
	p._createRequest = function() {
		this._request = new createjs.XHRRequest(this._item, false);
		this._request.setResponseType("arraybuffer");
	};

	p._sendComplete = function (event) {
		// OJR we leave this wrapped in Loader because we need to reference src and the handler only receives a single argument, the decodedAudio
		Loader.context.decodeAudioData(this._rawResult,
	         createjs.proxy(this._handleAudioDecoded, this),
	         createjs.proxy(this._sendError, this));
	};


	/**
	* The audio has been decoded.
	* @method handleAudioDecoded
	* @param decoded
	* @protected
	*/
	p._handleAudioDecoded = function (decodedAudio) {
		this._result = decodedAudio;
		this.AbstractLoader__sendComplete();
	};

	createjs.WebAudioLoader = createjs.promote(Loader, "AbstractLoader");
}());

//##############################################################################
// WebAudioSoundInstance.js
//##############################################################################

/**
 * WebAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
 * {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
 *
 * WebAudioSoundInstance exposes audioNodes for advanced users.
 *
 * @param {String} src The path to and file name of the sound.
 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
 * @param {Object} playbackResource Any resource needed by plugin to support audio playback.
 * @class WebAudioSoundInstance
 * @extends AbstractSoundInstance
 * @constructor
 */
(function () {
	"use strict";

	function WebAudioSoundInstance(src, startTime, duration, playbackResource) {
		this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);


// public properties
		/**
		 * NOTE this is only intended for use by advanced users.
		 * <br />GainNode for controlling <code>WebAudioSoundInstance</code> volume. Connected to the {{#crossLink "WebAudioSoundInstance/destinationNode:property"}}{{/crossLink}}.
		 * @property gainNode
		 * @type {AudioGainNode}
		 * @since 0.4.0
		 *
		 */
		this.gainNode = s.context.createGain();

		/**
		 * NOTE this is only intended for use by advanced users.
		 * <br />A panNode allowing left and right audio channel panning only. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
		 * @property panNode
		 * @type {AudioPannerNode}
		 * @since 0.4.0
		 */
		this.panNode = s.context.createPanner();
		this.panNode.panningModel = s._panningModel;
		this.panNode.connect(this.gainNode);
		this._updatePan();

		/**
		 * NOTE this is only intended for use by advanced users.
		 * <br />sourceNode is the audio source. Connected to WebAudioSoundInstance {{#crossLink "WebAudioSoundInstance/panNode:property"}}{{/crossLink}}.
		 * @property sourceNode
		 * @type {AudioNode}
		 * @since 0.4.0
		 *
		 */
		this.sourceNode = null;


// private properties
		/**
		 * Timeout that is created internally to handle sound playing to completion.
		 * Stored so we can remove it when stop, pause, or cleanup are called
		 * @property _soundCompleteTimeout
		 * @type {timeoutVariable}
		 * @default null
		 * @protected
		 * @since 0.4.0
		 */
		this._soundCompleteTimeout = null;

		/**
		 * NOTE this is only intended for use by very advanced users.
		 * _sourceNodeNext is the audio source for the next loop, inserted in a look ahead approach to allow for smooth
		 * looping. Connected to {{#crossLink "WebAudioSoundInstance/gainNode:property"}}{{/crossLink}}.
		 * @property _sourceNodeNext
		 * @type {AudioNode}
		 * @default null
		 * @protected
		 * @since 0.4.1
		 *
		 */
		this._sourceNodeNext = null;

		/**
		 * Time audio started playback, in seconds. Used to handle set position, get position, and resuming from paused.
		 * @property _playbackStartTime
		 * @type {Number}
		 * @default 0
		 * @protected
		 * @since 0.4.0
		 */
		this._playbackStartTime = 0;

		// Proxies, make removing listeners easier.
		this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
	};
	var p = createjs.extend(WebAudioSoundInstance, createjs.AbstractSoundInstance);
	var s = WebAudioSoundInstance;

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


	/**
	 * Note this is only intended for use by advanced users.
	 * <br />Audio context used to create nodes.  This is and needs to be the same context used by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
  	 * @property context
	 * @type {AudioContext}
	 * @static
	 * @since 0.6.0
	 */
	s.context = null;

	/**
	 * Note this is only intended for use by advanced users.
	 * <br />The scratch buffer that will be assigned to the buffer property of a source node on close.  
	 * This is and should be the same scratch buffer referenced by {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.
  	 * @property _scratchBuffer
	 * @type {AudioBufferSourceNode}
	 * @static
	 */
	s._scratchBuffer = null;

	/**
	 * Note this is only intended for use by advanced users.
	 * <br /> Audio node from WebAudioPlugin that sequences to <code>context.destination</code>
	 * @property destinationNode
	 * @type {AudioNode}
	 * @static
	 * @since 0.6.0
	 */
	s.destinationNode = null;

	/**
	 * Value to set panning model to equal power for WebAudioSoundInstance.  Can be "equalpower" or 0 depending on browser implementation.
	 * @property _panningModel
	 * @type {Number / String}
	 * @protected
	 * @static
	 * @since 0.6.0
	 */
	s._panningModel = "equalpower";


// Public methods
	p.destroy = function() {
		this.AbstractSoundInstance_destroy();

		this.panNode.disconnect(0);
		this.panNode = null;
		this.gainNode.disconnect(0);
		this.gainNode = null;
	};

	p.toString = function () {
		return "[WebAudioSoundInstance]";
	};


// Private Methods
	p._updatePan = function() {
		this.panNode.setPosition(this._pan, 0, -0.5);
		// z need to be -0.5 otherwise the sound only plays in left, right, or center
	};

	p._removeLooping = function(value) {
		this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
	};

	p._addLooping = function(value) {
		if (this.playState != createjs.Sound.PLAY_SUCCEEDED) { return; }
		this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
	};

	p._setDurationFromSource = function () {
		this._duration = this.playbackResource.duration * 1000;
	};

	p._handleCleanUp = function () {
		if (this.sourceNode && this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
			this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
		}

		if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}
		// OJR there appears to be a bug that this doesn't always work in webkit (Chrome and Safari). According to the documentation, this should work.

		clearTimeout(this._soundCompleteTimeout);

		this._playbackStartTime = 0;	// This is used by getPosition
	};

	/**
	 * Turn off and disconnect an audioNode, then set reference to null to release it for garbage collection
	 * @method _cleanUpAudioNode
	 * @param audioNode
	 * @return {audioNode}
	 * @protected
	 * @since 0.4.1
	 */
	p._cleanUpAudioNode = function(audioNode) {
		if(audioNode) {
			audioNode.stop(0);
			audioNode.disconnect(0);
			// necessary to prevent leak on iOS Safari 7-9. will throw in almost all other
			// browser implementations.
			try { audioNode.buffer = s._scratchBuffer; } catch(e) {}
			audioNode = null;
		}
		return audioNode;
	};

	p._handleSoundReady = function (event) {
		this.gainNode.connect(s.destinationNode);  // this line can cause a memory leak.  Nodes need to be disconnected from the audioDestination or any sequence that leads to it.

		var dur = this._duration * 0.001;
		var pos = this._position * 0.001;
		if (pos > dur) {pos = dur;}
		this.sourceNode = this._createAndPlayAudioNode((s.context.currentTime - dur), pos);
		this._playbackStartTime = this.sourceNode.startTime - pos;

		this._soundCompleteTimeout = setTimeout(this._endedHandler, (dur - pos) * 1000);

		if(this._loop != 0) {
			this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
		}
	};

	/**
	 * Creates an audio node using the current src and context, connects it to the gain node, and starts playback.
	 * @method _createAndPlayAudioNode
	 * @param {Number} startTime The time to add this to the web audio context, in seconds.
	 * @param {Number} offset The amount of time into the src audio to start playback, in seconds.
	 * @return {audioNode}
	 * @protected
	 * @since 0.4.1
	 */
	p._createAndPlayAudioNode = function(startTime, offset) {
		var audioNode = s.context.createBufferSource();
		audioNode.buffer = this.playbackResource;
		audioNode.connect(this.panNode);
		var dur = this._duration * 0.001;
		audioNode.startTime = startTime + dur;
		audioNode.start(audioNode.startTime, offset+(this._startTime*0.001), dur - offset);
		return audioNode;
	};

	p._pause = function () {
		this._position = (s.context.currentTime - this._playbackStartTime) * 1000;  // * 1000 to give milliseconds, lets us restart at same point
		this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
		this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);

		if (this.gainNode.numberOfOutputs != 0) {this.gainNode.disconnect(0);}

		clearTimeout(this._soundCompleteTimeout);
	};

	p._resume = function () {
		this._handleSoundReady();
	};

	/*
	p._handleStop = function () {
		// web audio does not need to do anything extra
	};
	*/

	p._updateVolume = function () {
		var newVolume = this._muted ? 0 : this._volume;
	  	if (newVolume != this.gainNode.gain.value) {
		  this.gainNode.gain.value = newVolume;
  		}
	};

	p._calculateCurrentPosition = function () {
		return ((s.context.currentTime - this._playbackStartTime) * 1000); // pos in seconds * 1000 to give milliseconds
	};

	p._updatePosition = function () {
		this.sourceNode = this._cleanUpAudioNode(this.sourceNode);
		this._sourceNodeNext = this._cleanUpAudioNode(this._sourceNodeNext);
		clearTimeout(this._soundCompleteTimeout);

		if (!this._paused) {this._handleSoundReady();}
	};

	// OJR we are using a look ahead approach to ensure smooth looping.
	// We add _sourceNodeNext to the audio context so that it starts playing even if this callback is delayed.
	// This technique is described here:  http://www.html5rocks.com/en/tutorials/audio/scheduling/
	// NOTE the cost of this is that our audio loop may not always match the loop event timing precisely.
	p._handleLoop = function () {
		this._cleanUpAudioNode(this.sourceNode);
		this.sourceNode = this._sourceNodeNext;
		this._playbackStartTime = this.sourceNode.startTime;
		this._sourceNodeNext = this._createAndPlayAudioNode(this._playbackStartTime, 0);
		this._soundCompleteTimeout = setTimeout(this._endedHandler, this._duration);
	};

	p._updateDuration = function () {
		if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this._pause();
			this._resume();
		}
	};

	createjs.WebAudioSoundInstance = createjs.promote(WebAudioSoundInstance, "AbstractSoundInstance");
}());

//##############################################################################
// WebAudioPlugin.js
//##############################################################################

(function () {

	"use strict";

	/**
	 * Play sounds using Web Audio in the browser. The WebAudioPlugin is currently the default plugin, and will be used
	 * anywhere that it is supported. To change plugin priority, check out the Sound API
	 * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} method.

	 * <h4>Known Browser and OS issues for Web Audio</h4>
	 * <b>Firefox 25</b>
	 * <li>
	 *     mp3 audio files do not load properly on all windows machines, reported <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=929969" target="_blank">here</a>.
	 *     <br />For this reason it is recommended to pass another FireFox-supported type (i.e. ogg) as the default
	 *     extension, until this bug is resolved
	 * </li>
	 *
	 * <b>Webkit (Chrome and Safari)</b>
	 * <li>
	 *     AudioNode.disconnect does not always seem to work.  This can cause the file size to grow over time if you
	 * 	   are playing a lot of audio files.
	 * </li>
	 *
	 * <b>iOS 6 limitations</b>
	 * <ul>
	 *     <li>
	 *         Sound is initially muted and will only unmute through play being called inside a user initiated event
	 *         (touch/click). Please read the mobile playback notes in the the {{#crossLink "Sound"}}{{/crossLink}}
	 *         class for a full overview of the limitations, and how to get around them.
	 *     </li>
	 *	   <li>
	 *	       A bug exists that will distort un-cached audio when a video element is present in the DOM. You can avoid
	 *	       this bug by ensuring the audio and video audio share the same sample rate.
	 *	   </li>
	 * </ul>
	 * @class WebAudioPlugin
	 * @extends AbstractPlugin
	 * @constructor
	 * @since 0.4.0
	 */
	function WebAudioPlugin() {
		this.AbstractPlugin_constructor();


// Private Properties
		/**
		 * Value to set panning model to equal power for WebAudioSoundInstance.  Can be "equalpower" or 0 depending on browser implementation.
		 * @property _panningModel
		 * @type {Number / String}
		 * @protected
		 */
		this._panningModel = s._panningModel;;

		/**
		 * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
		 * need to be created within this context.
		 * @property context
		 * @type {AudioContext}
		 */
		this.context = s.context;

		/**
		 * A DynamicsCompressorNode, which is used to improve sound quality and prevent audio distortion.
		 * It is connected to <code>context.destination</code>.
		 *
		 * Can be accessed by advanced users through createjs.Sound.activePlugin.dynamicsCompressorNode.
		 * @property dynamicsCompressorNode
		 * @type {AudioNode}
		 */
		this.dynamicsCompressorNode = this.context.createDynamicsCompressor();
		this.dynamicsCompressorNode.connect(this.context.destination);

		/**
		 * A GainNode for controlling master volume. It is connected to {{#crossLink "WebAudioPlugin/dynamicsCompressorNode:property"}}{{/crossLink}}.
		 *
		 * Can be accessed by advanced users through createjs.Sound.activePlugin.gainNode.
		 * @property gainNode
		 * @type {AudioGainNode}
		 */
		this.gainNode = this.context.createGain();
		this.gainNode.connect(this.dynamicsCompressorNode);
		createjs.WebAudioSoundInstance.destinationNode = this.gainNode;

		this._capabilities = s._capabilities;

		this._loaderClass = createjs.WebAudioLoader;
		this._soundInstanceClass = createjs.WebAudioSoundInstance;

		this._addPropsToClasses();
	}
	var p = createjs.extend(WebAudioPlugin, createjs.AbstractPlugin);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// Static Properties
	var s = WebAudioPlugin;
	/**
	 * The capabilities of the plugin. This is generated via the {{#crossLink "WebAudioPlugin/_generateCapabilities:method"}}{{/crossLink}}
	 * method and is used internally.
	 * @property _capabilities
	 * @type {Object}
	 * @default null
	 * @protected
	 * @static
	 */
	s._capabilities = null;

	/**
	 * Value to set panning model to equal power for WebAudioSoundInstance.  Can be "equalpower" or 0 depending on browser implementation.
	 * @property _panningModel
	 * @type {Number / String}
	 * @protected
	 * @static
	 */
	s._panningModel = "equalpower";

	/**
	 * The web audio context, which WebAudio uses to play audio. All nodes that interact with the WebAudioPlugin
	 * need to be created within this context.
	 *
	 * Advanced users can set this to an existing context, but <b>must</b> do so before they call
	 * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
	 *
	 * @property context
	 * @type {AudioContext}
	 * @static
	 */
	s.context = null;

	/**
	 * The scratch buffer that will be assigned to the buffer property of a source node on close.
	 * Works around an iOS Safari bug: https://github.com/CreateJS/SoundJS/issues/102
	 *
	 * Advanced users can set this to an existing source node, but <b>must</b> do so before they call
	 * {{#crossLink "Sound/registerPlugins"}}{{/crossLink}} or {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}.
	 *
	 * @property _scratchBuffer
	 * @type {AudioBuffer}
	 * @protected
	 * @static
	 */
	 s._scratchBuffer = null;

	/**
	 * Indicated whether audio on iOS has been unlocked, which requires a touchend/mousedown event that plays an
	 * empty sound.
	 * @property _unlocked
	 * @type {boolean}
	 * @since 0.6.2
	 * @private
	 */
	s._unlocked = false;


// Static Public Methods
	/**
	 * Determine if the plugin can be used in the current browser/OS.
	 * @method isSupported
	 * @return {Boolean} If the plugin can be initialized.
	 * @static
	 */
	s.isSupported = function () {
		// check if this is some kind of mobile device, Web Audio works with local protocol under PhoneGap and it is unlikely someone is trying to run a local file
		var isMobilePhoneGap = createjs.BrowserDetect.isIOS || createjs.BrowserDetect.isAndroid || createjs.BrowserDetect.isBlackberry;
		// OJR isMobile may be redundant with _isFileXHRSupported available.  Consider removing.
		if (location.protocol == "file:" && !isMobilePhoneGap && !this._isFileXHRSupported()) { return false; }  // Web Audio requires XHR, which is not usually available locally
		s._generateCapabilities();
		if (s.context == null) {return false;}
		return true;
	};

	/**
	 * Plays an empty sound in the web audio context.  This is used to enable web audio on iOS devices, as they
	 * require the first sound to be played inside of a user initiated event (touch/click).  This is called when
	 * {{#crossLink "WebAudioPlugin"}}{{/crossLink}} is initialized (by Sound {{#crossLink "Sound/initializeDefaultPlugins"}}{{/crossLink}}
	 * for example).
	 *
	 * <h4>Example</h4>
	 *
	 *     function handleTouch(event) {
	 *         createjs.WebAudioPlugin.playEmptySound();
	 *     }
	 *
	 * @method playEmptySound
	 * @static
	 * @since 0.4.1
	 */
	s.playEmptySound = function() {
		if (s.context == null) {return;}
		var source = s.context.createBufferSource();
		source.buffer = s._scratchBuffer;
		source.connect(s.context.destination);
		source.start(0, 0, 0);
	};


// Static Private Methods
	/**
	 * Determine if XHR is supported, which is necessary for web audio.
	 * @method _isFileXHRSupported
	 * @return {Boolean} If XHR is supported.
	 * @since 0.4.2
	 * @protected
	 * @static
	 */
	s._isFileXHRSupported = function() {
		// it's much easier to detect when something goes wrong, so let's start optimistically
		var supported = true;

		var xhr = new XMLHttpRequest();
		try {
			xhr.open("GET", "WebAudioPluginTest.fail", false); // loading non-existant file triggers 404 only if it could load (synchronous call)
		} catch (error) {
			// catch errors in cases where the onerror is passed by
			supported = false;
			return supported;
		}
		xhr.onerror = function() { supported = false; }; // cause irrelevant
		// with security turned off, we can get empty success results, which is actually a failed read (status code 0?)
		xhr.onload = function() { supported = this.status == 404 || (this.status == 200 || (this.status == 0 && this.response != "")); };
		try {
			xhr.send();
		} catch (error) {
			// catch errors in cases where the onerror is passed by
			supported = false;
		}

		return supported;
	};

	/**
	 * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
	 * method for an overview of plugin capabilities.
	 * @method _generateCapabilities
	 * @static
	 * @protected
	 */
	s._generateCapabilities = function () {
		if (s._capabilities != null) {return;}
		// Web Audio can be in any formats supported by the audio element, from http://www.w3.org/TR/webaudio/#AudioContext-section
		var t = document.createElement("audio");
		if (t.canPlayType == null) {return null;}

		if (s.context == null) {
			if (window.AudioContext) {
				s.context = new AudioContext();
			} else if (window.webkitAudioContext) {
				s.context = new webkitAudioContext();
			} else {
				return null;
			}
		}
		if (s._scratchBuffer == null) {
			s._scratchBuffer = s.context.createBuffer(1, 1, 22050);
		}

		s._compatibilitySetUp();

		// Listen for document level clicks to unlock WebAudio on iOS. See the _unlock method.
		if ("ontouchstart" in window && s.context.state != "running") {
			s._unlock(); // When played inside of a touch event, this will enable audio on iOS immediately.
			document.addEventListener("mousedown", s._unlock, true);
			document.addEventListener("touchend", s._unlock, true);
		}


		s._capabilities = {
			panning:true,
			volume:true,
			tracks:-1
		};

		// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
		var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
		var extensionMap = createjs.Sound.EXTENSION_MAP;
		for (var i = 0, l = supportedExtensions.length; i < l; i++) {
			var ext = supportedExtensions[i];
			var playType = extensionMap[ext] || ext;
			s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
		}  // OJR another way to do this might be canPlayType:"m4a", codex: mp4

		// 0=no output, 1=mono, 2=stereo, 4=surround, 6=5.1 surround.
		// See http://www.w3.org/TR/webaudio/#AudioChannelSplitter for more details on channels.
		if (s.context.destination.numberOfChannels < 2) {
			s._capabilities.panning = false;
		}
	};

	/**
	 * Set up compatibility if only deprecated web audio calls are supported.
	 * See http://www.w3.org/TR/webaudio/#DeprecationNotes
	 * Needed so we can support new browsers that don't support deprecated calls (Firefox) as well as old browsers that
	 * don't support new calls.
	 *
	 * @method _compatibilitySetUp
	 * @static
	 * @protected
	 * @since 0.4.2
	 */
	s._compatibilitySetUp = function() {
		s._panningModel = "equalpower";
		//assume that if one new call is supported, they all are
		if (s.context.createGain) { return; }

		// simple name change, functionality the same
		s.context.createGain = s.context.createGainNode;

		// source node, add to prototype
		var audioNode = s.context.createBufferSource();
		audioNode.__proto__.start = audioNode.__proto__.noteGrainOn;	// note that noteGrainOn requires all 3 parameters
		audioNode.__proto__.stop = audioNode.__proto__.noteOff;

		// panningModel
		s._panningModel = 0;
	};

	/**
	 * Try to unlock audio on iOS. This is triggered from either WebAudio plugin setup (which will work if inside of
	 * a `mousedown` or `touchend` event stack), or the first document touchend/mousedown event. If it fails (touchend
	 * will fail if the user presses for too long, indicating a scroll event instead of a click event.
	 *
	 * Note that earlier versions of iOS supported `touchstart` for this, but iOS9 removed this functionality. Adding
	 * a `touchstart` event to support older platforms may preclude a `mousedown` even from getting fired on iOS9, so we
	 * stick with `mousedown` and `touchend`.
	 * @method _unlock
	 * @since 0.6.2
	 * @private
	 */
	s._unlock = function() {
		if (s._unlocked) { return; }
		s.playEmptySound();
		if (s.context.state == "running") {
			document.removeEventListener("mousedown", s._unlock, true);
			document.removeEventListener("touchend", s._unlock, true);
			s._unlocked = true;
		}
	};


// Public Methods
	p.toString = function () {
		return "[WebAudioPlugin]";
	};


// Private Methods
	/**
	 * Set up needed properties on supported classes WebAudioSoundInstance and WebAudioLoader.
	 * @method _addPropsToClasses
	 * @static
	 * @protected
	 * @since 0.6.0
	 */
	p._addPropsToClasses = function() {
		var c = this._soundInstanceClass;
		c.context = this.context;
		c._scratchBuffer = s._scratchBuffer;
		c.destinationNode = this.gainNode;
		c._panningModel = this._panningModel;

		this._loaderClass.context = this.context;
	};


	/**
	 * Set the gain value for master audio. Should not be called externally.
	 * @method _updateVolume
	 * @protected
	 */
	p._updateVolume = function () {
		var newVolume = createjs.Sound._masterMute ? 0 : this._volume;
		if (newVolume != this.gainNode.gain.value) {
			this.gainNode.gain.value = newVolume;
		}
	};

	createjs.WebAudioPlugin = createjs.promote(WebAudioPlugin, "AbstractPlugin");
}());

//##############################################################################
// HTMLAudioTagPool.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * HTMLAudioTagPool is an object pool for HTMLAudio tag instances.
	 * @class HTMLAudioTagPool
	 * @param {String} src The source of the channel.
	 * @protected
	 */
	function HTMLAudioTagPool() {
			throw "HTMLAudioTagPool cannot be instantiated";
	}

	var s = HTMLAudioTagPool;

// Static Properties
	/**
	 * A hash lookup of each base audio tag, indexed by the audio source.
	 * @property _tags
	 * @type {{}}
	 * @static
	 * @protected
	 */
	s._tags = {};

	/**
	 * An object pool for html audio tags
	 * @property _tagPool
	 * @type {TagPool}
	 * @static
	 * @protected
	 */
	s._tagPool = new TagPool();

	/**
	 * A hash lookup of if a base audio tag is available, indexed by the audio source
	 * @property _tagsUsed
	 * @type {{}}
	 * @protected
	 * @static
	 */
	s._tagUsed = {};

// Static Methods
	/**
	  * Get an audio tag with the given source.
	  * @method get
	  * @param {String} src The source file used by the audio tag.
	  * @static
	  */
	 s.get = function (src) {
		var t = s._tags[src];
		if (t == null) {
			// create new base tag
			t = s._tags[src] = s._tagPool.get();
			t.src = src;
		} else {
			// get base or pool
			if (s._tagUsed[src]) {
				t = s._tagPool.get();
				t.src = src;
			} else {
				s._tagUsed[src] = true;
			}
		}
		return t;
	 };

	 /**
	  * Return an audio tag to the pool.
	  * @method set
	  * @param {String} src The source file used by the audio tag.
	  * @param {HTMLElement} tag Audio tag to set.
	  * @static
	  */
	 s.set = function (src, tag) {
		 // check if this is base, if yes set boolean if not return to pool
		 if(tag == s._tags[src]) {
			 s._tagUsed[src] = false;
		 } else {
			 s._tagPool.set(tag);
		 }
	 };

	/**
	 * Delete stored tag reference and return them to pool. Note that if the tag reference does not exist, this will fail.
	 * @method remove
	 * @param {String} src The source for the tag
	 * @return {Boolean} If the TagPool was deleted.
	 * @static
	 */
	s.remove = function (src) {
		var tag = s._tags[src];
		if (tag == null) {return false;}
		s._tagPool.set(tag);
		delete(s._tags[src]);
		delete(s._tagUsed[src]);
		return true;
	};

	/**
	 * Gets the duration of the src audio in milliseconds
	 * @method getDuration
	 * @param {String} src The source file used by the audio tag.
	 * @return {Number} Duration of src in milliseconds
	 * @static
	 */
	s.getDuration= function (src) {
		var t = s._tags[src];
		if (t == null || !t.duration) {return 0;}	// OJR duration is NaN if loading has not completed
		return t.duration * 1000;
	};

	createjs.HTMLAudioTagPool = HTMLAudioTagPool;


// ************************************************************************************************************
	/**
	 * The TagPool is an object pool for HTMLAudio tag instances.
	 * #class TagPool
	 * @param {String} src The source of the channel.
	 * @protected
	 */
	function TagPool(src) {

// Public Properties
		/**
		 * A list of all available tags in the pool.
		 * #property tags
		 * @type {Array}
		 * @protected
		 */
		this._tags = [];
	};

	var p = TagPool.prototype;
	p.constructor = TagPool;


// Public Methods
	/**
	 * Get an HTMLAudioElement for immediate playback. This takes it out of the pool.
	 * #method get
	 * @return {HTMLAudioElement} An HTML audio tag.
	 */
	p.get = function () {
		var tag;
		if (this._tags.length == 0) {
			tag = this._createTag();
		} else {
			tag = this._tags.pop();
		}
		if (tag.parentNode == null) {document.body.appendChild(tag);}
		return tag;
	};

	/**
	 * Put an HTMLAudioElement back in the pool for use.
	 * #method set
	 * @param {HTMLAudioElement} tag HTML audio tag
	 */
	p.set = function (tag) {
		// OJR this first step seems unnecessary
		var index = createjs.indexOf(this._tags, tag);
		if (index == -1) {
			this._tags.src = null;
			this._tags.push(tag);
		}
	};

	p.toString = function () {
		return "[TagPool]";
	};


// Private Methods
	/**
	 * Create an HTML audio tag.
	 * #method _createTag
	 * @param {String} src The source file to set for the audio tag.
	 * @return {HTMLElement} Returns an HTML audio tag.
	 * @protected
	 */
	p._createTag = function () {
		var tag = document.createElement("audio");
		tag.autoplay = false;
		tag.preload = "none";
		//LM: Firefox fails when this the preload="none" for other tags, but it needs to be "none" to ensure PreloadJS works.
		return tag;
	};

}());

//##############################################################################
// HTMLAudioSoundInstance.js
//##############################################################################

(function () {
	"use strict";

	/**
	 * HTMLAudioSoundInstance extends the base api of {{#crossLink "AbstractSoundInstance"}}{{/crossLink}} and is used by
	 * {{#crossLink "HTMLAudioPlugin"}}{{/crossLink}}.
	 *
	 * @param {String} src The path to and file name of the sound.
	 * @param {Number} startTime Audio sprite property used to apply an offset, in milliseconds.
	 * @param {Number} duration Audio sprite property used to set the time the clip plays for, in milliseconds.
	 * @param {Object} playbackResource Any resource needed by plugin to support audio playback.
	 * @class HTMLAudioSoundInstance
	 * @extends AbstractSoundInstance
	 * @constructor
	 */
	function HTMLAudioSoundInstance(src, startTime, duration, playbackResource) {
		this.AbstractSoundInstance_constructor(src, startTime, duration, playbackResource);


// Private Properties
		this._audioSpriteStopTime = null;
		this._delayTimeoutId = null;

		// Proxies, make removing listeners easier.
		this._endedHandler = createjs.proxy(this._handleSoundComplete, this);
		this._readyHandler = createjs.proxy(this._handleTagReady, this);
		this._stalledHandler = createjs.proxy(this._playFailed, this);
		this._audioSpriteEndHandler = createjs.proxy(this._handleAudioSpriteLoop, this);
		this._loopHandler = createjs.proxy(this._handleSoundComplete, this);

		if (duration) {
			this._audioSpriteStopTime = (startTime + duration) * 0.001;
		} else {
			this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
		}
	}
	var p = createjs.extend(HTMLAudioSoundInstance, createjs.AbstractSoundInstance);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// Public Methods
	/**
	 * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master volume.
	 * undoc'd because it is not meant to be used outside of Sound
	 * #method setMasterVolume
	 * @param value
	 */
	p.setMasterVolume = function (value) {
		this._updateVolume();
	};

	/**
	 * Called by {{#crossLink "Sound"}}{{/crossLink}} when plugin does not handle master mute.
	 * undoc'd because it is not meant to be used outside of Sound
	 * #method setMasterMute
	 * @param value
	 */
	p.setMasterMute = function (isMuted) {
		this._updateVolume();
	};

	p.toString = function () {
		return "[HTMLAudioSoundInstance]";
	};

//Private Methods
	p._removeLooping = function() {
		if(this._playbackResource == null) {return;}
		this._playbackResource.loop = false;
		this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
	};

	p._addLooping = function() {
		if(this._playbackResource == null  || this._audioSpriteStopTime) {return;}
		this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
		this._playbackResource.loop = true;
	};

	p._handleCleanUp = function () {
		var tag = this._playbackResource;
		if (tag != null) {
			tag.pause();
			tag.loop = false;
			tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
			tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
			tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
			tag.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
			tag.removeEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);

			try {
				tag.currentTime = this._startTime;
			} catch (e) {
			} // Reset Position
			createjs.HTMLAudioTagPool.set(this.src, tag);
			this._playbackResource = null;
		}
	};

	p._beginPlaying = function (playProps) {
		this._playbackResource = createjs.HTMLAudioTagPool.get(this.src);
		return this.AbstractSoundInstance__beginPlaying(playProps);
	};

	p._handleSoundReady = function (event) {
		if (this._playbackResource.readyState !== 4) {
			var tag = this._playbackResource;
			tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
			tag.addEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);
			tag.preload = "auto"; // This is necessary for Firefox, as it won't ever "load" until this is set.
			tag.load();
			return;
		}

		this._updateVolume();
		this._playbackResource.currentTime = (this._startTime + this._position) * 0.001;
		if (this._audioSpriteStopTime) {
			this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
		} else {
			this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
			if(this._loop != 0) {
				this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
				this._playbackResource.loop = true;
			}
		}

		this._playbackResource.play();
	};

	/**
	 * Used to handle when a tag is not ready for immediate playback when it is returned from the HTMLAudioTagPool.
	 * @method _handleTagReady
	 * @param event
	 * @protected
	 */
	p._handleTagReady = function (event) {
		this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_READY, this._readyHandler, false);
		this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_STALLED, this._stalledHandler, false);

		this._handleSoundReady();
	};

	p._pause = function () {
		this._playbackResource.pause();
	};

	p._resume = function () {
		this._playbackResource.play();
	};

	p._updateVolume = function () {
		if (this._playbackResource != null) {
			var newVolume = (this._muted || createjs.Sound._masterMute) ? 0 : this._volume * createjs.Sound._masterVolume;
			if (newVolume != this._playbackResource.volume) {this._playbackResource.volume = newVolume;}
		}
	};

	p._calculateCurrentPosition = function() {
		return (this._playbackResource.currentTime * 1000) - this._startTime;
	};

	p._updatePosition = function() {
		this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
		this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
		try {
			this._playbackResource.currentTime = (this._position + this._startTime) * 0.001;
		} catch (error) { // Out of range
			this._handleSetPositionSeek(null);
		}
	};

	/**
	 * Used to enable setting position, as we need to wait for that seek to be done before we add back our loop handling seek listener
	 * @method _handleSetPositionSeek
	 * @param event
	 * @protected
	 */
	p._handleSetPositionSeek = function(event) {
		if (this._playbackResource == null) { return; }
		this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._handleSetPositionSeek, false);
		this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
	};

	/**
	 * Timer used to loop audio sprites.
	 * NOTE because of the inaccuracies in the timeupdate event (15 - 250ms) and in setting the tag to the desired timed
	 * (up to 300ms), it is strongly recommended not to loop audio sprites with HTML Audio if smooth looping is desired
	 *
	 * @method _handleAudioSpriteLoop
	 * @param event
	 * @private
	 */
	p._handleAudioSpriteLoop = function (event) {
		if(this._playbackResource.currentTime <= this._audioSpriteStopTime) {return;}
		this._playbackResource.pause();
		if(this._loop == 0) {
			this._handleSoundComplete(null);
		} else {
			this._position = 0;
			this._loop--;
			this._playbackResource.currentTime = this._startTime * 0.001;
			if(!this._paused) {this._playbackResource.play();}
			this._sendEvent("loop");
		}
	};

	// NOTE with this approach audio will loop as reliably as the browser allows
	// but we could end up sending the loop event after next loop playback begins
	p._handleLoop = function (event) {
		if(this._loop == 0) {
			this._playbackResource.loop = false;
			this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_SEEKED, this._loopHandler, false);
		}
	};

	p._updateStartTime = function () {
		this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;

		if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
			this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
		}
	};

	p._updateDuration = function () {
		this._audioSpriteStopTime = (this._startTime + this._duration) * 0.001;

		if(this.playState == createjs.Sound.PLAY_SUCCEEDED) {
			this._playbackResource.removeEventListener(createjs.HTMLAudioPlugin._AUDIO_ENDED, this._endedHandler, false);
			this._playbackResource.addEventListener(createjs.HTMLAudioPlugin._TIME_UPDATE, this._audioSpriteEndHandler, false);
		}
	};

	p._setDurationFromSource = function () {
		this._duration = createjs.HTMLAudioTagPool.getDuration(this.src);
		this._playbackResource = null;
	};

	createjs.HTMLAudioSoundInstance = createjs.promote(HTMLAudioSoundInstance, "AbstractSoundInstance");
}());

//##############################################################################
// HTMLAudioPlugin.js
//##############################################################################

(function () {

	"use strict";

	/**
	 * Play sounds using HTML &lt;audio&gt; tags in the browser. This plugin is the second priority plugin installed
	 * by default, after the {{#crossLink "WebAudioPlugin"}}{{/crossLink}}.  For older browsers that do not support html
	 * audio, include and install the {{#crossLink "FlashAudioPlugin"}}{{/crossLink}}.
	 *
	 * <h4>Known Browser and OS issues for HTML Audio</h4>
	 * <b>All browsers</b><br />
	 * Testing has shown in all browsers there is a limit to how many audio tag instances you are allowed.  If you exceed
	 * this limit, you can expect to see unpredictable results. Please use {{#crossLink "Sound.MAX_INSTANCES"}}{{/crossLink}} as
	 * a guide to how many total audio tags you can safely use in all browsers.  This issue is primarily limited to IE9.
	 *
     * <b>IE html limitations</b><br />
     * <ul><li>There is a delay in applying volume changes to tags that occurs once playback is started. So if you have
     * muted all sounds, they will all play during this delay until the mute applies internally. This happens regardless of
     * when or how you apply the volume change, as the tag seems to need to play to apply it.</li>
     * <li>MP3 encoding will not always work for audio tags if it's not default.  We've found default encoding with
     * 64kbps works.</li>
	 * <li>Occasionally very short samples will get cut off.</li>
	 * <li>There is a limit to how many audio tags you can load or play at once, which appears to be determined by
	 * hardware and browser settings.  See {{#crossLink "HTMLAudioPlugin.MAX_INSTANCES"}}{{/crossLink}} for a safe estimate.
	 * Note that audio sprites can be used as a solution to this issue.</li></ul>
	 *
	 * <b>Safari limitations</b><br />
	 * <ul><li>Safari requires Quicktime to be installed for audio playback.</li></ul>
	 *
	 * <b>iOS 6 limitations</b><br />
	 * <ul><li>can only have one &lt;audio&gt; tag</li>
	 * 		<li>can not preload or autoplay the audio</li>
	 * 		<li>can not cache the audio</li>
	 * 		<li>can not play the audio except inside a user initiated event.</li>
	 *		<li>Note it is recommended to use {{#crossLink "WebAudioPlugin"}}{{/crossLink}} for iOS (6+)</li>
	 * 		<li>audio sprites can be used to mitigate some of these issues and are strongly recommended on iOS</li>
	 * </ul>
	 *
	 * <b>Android Native Browser limitations</b><br />
	 * <ul><li>We have no control over audio volume. Only the user can set volume on their device.</li>
	 *      <li>We can only play audio inside a user event (touch/click).  This currently means you cannot loop sound or use a delay.</li></ul>
	 * <b> Android Chrome 26.0.1410.58 specific limitations</b><br />
	 * <ul> <li>Can only play 1 sound at a time.</li>
	 *      <li>Sound is not cached.</li>
	 *      <li>Sound can only be loaded in a user initiated touch/click event.</li>
	 *      <li>There is a delay before a sound is played, presumably while the src is loaded.</li>
	 * </ul>
	 *
	 * See {{#crossLink "Sound"}}{{/crossLink}} for general notes on known issues.
	 *
	 * @class HTMLAudioPlugin
	 * @extends AbstractPlugin
	 * @constructor
	 */
	function HTMLAudioPlugin() {
		this.AbstractPlugin_constructor();


	// Public Properties
		/**
		 * This is no longer needed as we are now using object pooling for tags.
		 *
		 * <b>NOTE this property only exists as a limitation of HTML audio.</b>
		 * @property defaultNumChannels
		 * @type {Number}
		 * @default 2
		 * @since 0.4.0
		 * @deprecated
		 */
		this.defaultNumChannels = 2;

		this._capabilities = s._capabilities;

		this._loaderClass = createjs.SoundLoader;
		this._soundInstanceClass = createjs.HTMLAudioSoundInstance;
	}

	var p = createjs.extend(HTMLAudioPlugin, createjs.AbstractPlugin);
	var s = HTMLAudioPlugin;

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.


// Static Properties
	/**
	 * The maximum number of instances that can be loaded or played. This is a browser limitation, primarily limited to IE9.
	 * The actual number varies from browser to browser (and is largely hardware dependant), but this is a safe estimate.
	 * Audio sprites work around this limitation.
	 * @property MAX_INSTANCES
	 * @type {Number}
	 * @default 30
	 * @static
	 */
	s.MAX_INSTANCES = 30;

	/**
	 * Event constant for the "canPlayThrough" event for cleaner code.
	 * @property _AUDIO_READY
	 * @type {String}
	 * @default canplaythrough
	 * @static
	 * @protected
	 */
	s._AUDIO_READY = "canplaythrough";

	/**
	 * Event constant for the "ended" event for cleaner code.
	 * @property _AUDIO_ENDED
	 * @type {String}
	 * @default ended
	 * @static
	 * @protected
	 */
	s._AUDIO_ENDED = "ended";

	/**
	 * Event constant for the "seeked" event for cleaner code.  We utilize this event for maintaining loop events.
	 * @property _AUDIO_SEEKED
	 * @type {String}
	 * @default seeked
	 * @static
	 * @protected
	 */
	s._AUDIO_SEEKED = "seeked";

	/**
	 * Event constant for the "stalled" event for cleaner code.
	 * @property _AUDIO_STALLED
	 * @type {String}
	 * @default stalled
	 * @static
	 * @protected
	 */
	s._AUDIO_STALLED = "stalled";

	/**
	 * Event constant for the "timeupdate" event for cleaner code.  Utilized for looping audio sprites.
	 * This event callsback ever 15 to 250ms and can be dropped by the browser for performance.
	 * @property _TIME_UPDATE
	 * @type {String}
	 * @default timeupdate
	 * @static
	 * @protected
	 */
	s._TIME_UPDATE = "timeupdate";

	/**
	 * The capabilities of the plugin. This is generated via the {{#crossLink "HTMLAudioPlugin/_generateCapabilities"}}{{/crossLink}}
	 * method. Please see the Sound {{#crossLink "Sound/getCapabilities"}}{{/crossLink}} method for an overview of all
	 * of the available properties.
	 * @property _capabilities
	 * @type {Object}
	 * @protected
	 * @static
	 */
	s._capabilities = null;


// Static Methods
	/**
	 * Determine if the plugin can be used in the current browser/OS. Note that HTML audio is available in most modern
	 * browsers, but is disabled in iOS because of its limitations.
	 * @method isSupported
	 * @return {Boolean} If the plugin can be initialized.
	 * @static
	 */
	s.isSupported = function () {
		s._generateCapabilities();
		return (s._capabilities != null);
	};

	/**
	 * Determine the capabilities of the plugin. Used internally. Please see the Sound API {{#crossLink "Sound/getCapabilities"}}{{/crossLink}}
	 * method for an overview of plugin capabilities.
	 * @method _generateCapabilities
	 * @static
	 * @protected
	 */
	s._generateCapabilities = function () {
		if (s._capabilities != null) {return;}
		var t = document.createElement("audio");
		if (t.canPlayType == null) {return null;}

		s._capabilities = {
			panning:false,
			volume:true,
			tracks:-1
		};

		// determine which extensions our browser supports for this plugin by iterating through Sound.SUPPORTED_EXTENSIONS
		var supportedExtensions = createjs.Sound.SUPPORTED_EXTENSIONS;
		var extensionMap = createjs.Sound.EXTENSION_MAP;
		for (var i = 0, l = supportedExtensions.length; i < l; i++) {
			var ext = supportedExtensions[i];
			var playType = extensionMap[ext] || ext;
			s._capabilities[ext] = (t.canPlayType("audio/" + ext) != "no" && t.canPlayType("audio/" + ext) != "") || (t.canPlayType("audio/" + playType) != "no" && t.canPlayType("audio/" + playType) != "");
		}  // OJR another way to do this might be canPlayType:"m4a", codex: mp4
	};


// public methods
	p.register = function (loadItem) {
		var tag = createjs.HTMLAudioTagPool.get(loadItem.src);
		var loader = this.AbstractPlugin_register(loadItem);
		loader.setTag(tag);

		return loader;
	};

	p.removeSound = function (src) {
		this.AbstractPlugin_removeSound(src);
		createjs.HTMLAudioTagPool.remove(src);
	};

	p.create = function (src, startTime, duration) {
		var si = this.AbstractPlugin_create(src, startTime, duration);
		si.setPlaybackResource(null);
		return si;
	};

	p.toString = function () {
		return "[HTMLAudioPlugin]";
	};

	// plugin does not support these
	p.setVolume = p.getVolume = p.setMute = null;


	createjs.HTMLAudioPlugin = createjs.promote(HTMLAudioPlugin, "AbstractPlugin");
}());

//##############################################################################
// Tween.js
//##############################################################################

// TODO: possibly add a END actionsMode (only runs actions that == position)?
// TODO: evaluate a way to decouple paused from tick registration.




(function() {
	"use strict";


// constructor
	/**
	 * A Tween instance tweens properties for a single target. Instance methods can be chained for easy construction and sequencing:
	 *
	 * <h4>Example</h4>
	 *
	 *      target.alpha = 1;
	 *	    createjs.Tween.get(target)
	 *	         .wait(500)
	 *	         .to({alpha:0, visible:false}, 1000)
	 *	         .call(handleComplete);
	 *	    function handleComplete() {
	 *	    	//Tween complete
	 *	    }
	 *
	 * Multiple tweens can point to the same instance, however if they affect the same properties there could be unexpected
	 * behaviour. To stop all tweens on an object, use {{#crossLink "Tween/removeTweens"}}{{/crossLink}} or pass `override:true`
	 * in the props argument.
	 *
	 *      createjs.Tween.get(target, {override:true}).to({x:100});
	 *
	 * Subscribe to the {{#crossLink "Tween/change:event"}}{{/crossLink}} event to get notified when a property of the
	 * target is changed.
	 *
	 *      createjs.Tween.get(target, {override:true}).to({x:100}).addEventListener("change", handleChange);
	 *      function handleChange(event) {
	 *          // The tween changed.
	 *      }
	 *
	 * See the Tween {{#crossLink "Tween/get"}}{{/crossLink}} method for additional param documentation.
	 * @class Tween
	 * @param {Object} target The target object that will have its properties tweened.
	 * @param {Object} [props] The configuration properties to apply to this tween instance (ex. `{loop:true, paused:true}`.
	 * All properties default to false. Supported props are:<UL>
	 *    <LI> loop: sets the loop property on this tween.</LI>
	 *    <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI>
	 *    <LI> ignoreGlobalPause: sets the {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} property on this tween.</LI>
	 *    <LI> override: if true, `Tween.removeTweens(target)` will be called to remove any other tweens with the same target.
	 *    <LI> paused: indicates whether to start the tween paused.</LI>
	 *    <LI> position: indicates the initial position for this tween.</LI>
	 *    <LI> onChange: specifies a listener for the "change" event.</LI>
	 * </UL>
	 * @param {Object} [pluginData] An object containing data for use by installed plugins. See individual
	 * plugins' documentation for details.
	 * @extends EventDispatcher
	 * @constructor
	 */
	function Tween(target, props, pluginData) {

	// public properties:
		/**
		 * Causes this tween to continue playing when a global pause is active. For example, if TweenJS is using {{#crossLink "Ticker"}}{{/crossLink}},
		 * then setting this to true (the default) will cause this tween to be paused when <code>Ticker.setPaused(true)</code>
		 * is called. See the Tween {{#crossLink "Tween/tick"}}{{/crossLink}} method for more info. Can be set via the props
		 * parameter.
		 * @property ignoreGlobalPause
		 * @type Boolean
		 * @default false
		 */
		this.ignoreGlobalPause = false;
	
		/**
		 * If true, the tween will loop when it reaches the end. Can be set via the props param.
		 * @property loop
		 * @type {Boolean}
		 * @default false
		 */
		this.loop = false;
	
		/**
		 * Specifies the total duration of this tween in milliseconds (or ticks if useTicks is true).
		 * This value is automatically updated as you modify the tween. Changing it directly could result in unexpected
		 * behaviour.
		 * @property duration
		 * @type {Number}
		 * @default 0
		 * @readonly
		 */
		this.duration = 0;
	
		/**
		 * Allows you to specify data that will be used by installed plugins. Each plugin uses this differently, but in general
		 * you specify data by setting it to a property of pluginData with the same name as the plugin class.
		 * @example
		 *	myTween.pluginData.PluginClassName = data;
		 * <br/>
		 * Also, most plugins support a property to enable or disable them. This is typically the plugin class name followed by "_enabled".<br/>
		 * @example
		 *	myTween.pluginData.PluginClassName_enabled = false;<br/>
		 * <br/>
		 * Some plugins also store instance data in this object, usually in a property named _PluginClassName.
		 * See the documentation for individual plugins for more details.
		 * @property pluginData
		 * @type {Object}
		 */
		this.pluginData = pluginData || {};
	
		/**
		 * The target of this tween. This is the object on which the tweened properties will be changed. Changing
		 * this property after the tween is created will not have any effect.
		 * @property target
		 * @type {Object}
		 * @readonly
		 */
		this.target = target;
	
		/**
		 * The current normalized position of the tween. This will always be a value between 0 and duration.
		 * Changing this property directly will have no effect.
		 * @property position
		 * @type {Object}
		 * @readonly
		 */
		this.position = null;
	
		/**
		 * Indicates the tween's current position is within a passive wait.
		 * @property passive
		 * @type {Boolean}
		 * @default false
		 * @readonly
		 **/
		this.passive = false;
	
	// private properties:	
		/**
		 * @property _paused
		 * @type {Boolean}
		 * @default false
		 * @protected
		 */
		this._paused = false;
	
		/**
		 * @property _curQueueProps
		 * @type {Object}
		 * @protected
		 */
		this._curQueueProps = {};
	
		/**
		 * @property _initQueueProps
		 * @type {Object}
		 * @protected
		 */
		this._initQueueProps = {};
	
		/**
		 * @property _steps
		 * @type {Array}
		 * @protected
		 */
		this._steps = [];
	
		/**
		 * @property _actions
		 * @type {Array}
		 * @protected
		 */
		this._actions = [];
	
		/**
		 * Raw position.
		 * @property _prevPosition
		 * @type {Number}
		 * @default 0
		 * @protected
		 */
		this._prevPosition = 0;
	
		/**
		 * The position within the current step.
		 * @property _stepPosition
		 * @type {Number}
		 * @default 0
		 * @protected
		 */
		this._stepPosition = 0; // this is needed by MovieClip.
	
		/**
		 * Normalized position.
		 * @property _prevPos
		 * @type {Number}
		 * @default -1
		 * @protected
		 */
		this._prevPos = -1;
	
		/**
		 * @property _target
		 * @type {Object}
		 * @protected
		 */
		this._target = target;
	
		/**
		 * @property _useTicks
		 * @type {Boolean}
		 * @default false
		 * @protected
		 */
		this._useTicks = false;
	
		/**
		 * @property _inited
		 * @type {boolean}
		 * @default false
		 * @protected
		 */
		this._inited = false;
		
		/**
		 * Indicates whether the tween is currently registered with Tween.
		 * @property _registered
		 * @type {boolean}
		 * @default false
		 * @protected
		 */
		this._registered = false;


		if (props) {
			this._useTicks = props.useTicks;
			this.ignoreGlobalPause = props.ignoreGlobalPause;
			this.loop = props.loop;
			props.onChange && this.addEventListener("change", props.onChange);
			if (props.override) { Tween.removeTweens(target); }
		}
		if (props&&props.paused) { this._paused=true; }
		else { createjs.Tween._register(this,true); }
		if (props&&props.position!=null) { this.setPosition(props.position, Tween.NONE); }

	};

	var p = createjs.extend(Tween, createjs.EventDispatcher);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.
	

// static properties
	/**
	 * Constant defining the none actionsMode for use with setPosition.
	 * @property NONE
	 * @type Number
	 * @default 0
	 * @static
	 */
	Tween.NONE = 0;

	/**
	 * Constant defining the loop actionsMode for use with setPosition.
	 * @property LOOP
	 * @type Number
	 * @default 1
	 * @static
	 */
	Tween.LOOP = 1;

	/**
	 * Constant defining the reverse actionsMode for use with setPosition.
	 * @property REVERSE
	 * @type Number
	 * @default 2
	 * @static
	 */
	Tween.REVERSE = 2;

	/**
	 * Constant returned by plugins to tell the tween not to use default assignment.
	 * @property IGNORE
	 * @type Object
	 * @static
	 */
	Tween.IGNORE = {};

	/**
	 * @property _listeners
	 * @type Array[Tween]
	 * @static
	 * @protected
	 */
	Tween._tweens = [];

	/**
	 * @property _plugins
	 * @type Object
	 * @static
	 * @protected
	 */
	Tween._plugins = {};


// static methods	
	/**
	 * Returns a new tween instance. This is functionally identical to using "new Tween(...)", but looks cleaner
	 * with the chained syntax of TweenJS.
	 * <h4>Example</h4>
	 *
	 *		var tween = createjs.Tween.get(target);
	 *
	 * @method get
	 * @param {Object} target The target object that will have its properties tweened.
	 * @param {Object} [props] The configuration properties to apply to this tween instance (ex. `{loop:true, paused:true}`).
	 * All properties default to `false`. Supported props are:
	 * <UL>
	 *    <LI> loop: sets the loop property on this tween.</LI>
	 *    <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI>
	 *    <LI> ignoreGlobalPause: sets the {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} property on
	 *    this tween.</LI>
	 *    <LI> override: if true, `createjs.Tween.removeTweens(target)` will be called to remove any other tweens with
	 *    the same target.
	 *    <LI> paused: indicates whether to start the tween paused.</LI>
	 *    <LI> position: indicates the initial position for this tween.</LI>
	 *    <LI> onChange: specifies a listener for the {{#crossLink "Tween/change:event"}}{{/crossLink}} event.</LI>
	 * </UL>
	 * @param {Object} [pluginData] An object containing data for use by installed plugins. See individual plugins'
	 * documentation for details.
	 * @param {Boolean} [override=false] If true, any previous tweens on the same target will be removed. This is the
	 * same as calling `Tween.removeTweens(target)`.
	 * @return {Tween} A reference to the created tween. Additional chained tweens, method calls, or callbacks can be
	 * applied to the returned tween instance.
	 * @static
	 */
	Tween.get = function(target, props, pluginData, override) {
		if (override) { Tween.removeTweens(target); }
		return new Tween(target, props, pluginData);
	};

	/**
	 * Advances all tweens. This typically uses the {{#crossLink "Ticker"}}{{/crossLink}} class, but you can call it
	 * manually if you prefer to use your own "heartbeat" implementation.
	 * @method tick
	 * @param {Number} delta The change in time in milliseconds since the last tick. Required unless all tweens have
	 * `useTicks` set to true.
	 * @param {Boolean} paused Indicates whether a global pause is in effect. Tweens with {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}}
	 * will ignore this, but all others will pause if this is `true`.
	 * @static
	 */
	Tween.tick = function(delta, paused) {
		var tweens = Tween._tweens.slice(); // to avoid race conditions.
		for (var i=tweens.length-1; i>=0; i--) {
			var tween = tweens[i];
			if ((paused && !tween.ignoreGlobalPause) || tween._paused) { continue; }
			tween.tick(tween._useTicks?1:delta);
		}
	};

	/**
	 * Handle events that result from Tween being used as an event handler. This is included to allow Tween to handle
	 * {{#crossLink "Ticker/tick:event"}}{{/crossLink}} events from the createjs {{#crossLink "Ticker"}}{{/crossLink}}.
	 * No other events are handled in Tween.
	 * @method handleEvent
	 * @param {Object} event An event object passed in by the {{#crossLink "EventDispatcher"}}{{/crossLink}}. Will
	 * usually be of type "tick".
	 * @private
	 * @static
	 * @since 0.4.2
	 */
	Tween.handleEvent = function(event) {
		if (event.type == "tick") {
			this.tick(event.delta, event.paused);
		}
	};

	/**
	 * Removes all existing tweens for a target. This is called automatically by new tweens if the `override`
	 * property is `true`.
	 * @method removeTweens
	 * @param {Object} target The target object to remove existing tweens from.
	 * @static
	 */
	Tween.removeTweens = function(target) {
		if (!target.tweenjs_count) { return; }
		var tweens = Tween._tweens;
		for (var i=tweens.length-1; i>=0; i--) {
			var tween = tweens[i];
			if (tween._target == target) {
				tween._paused = true;
				tweens.splice(i, 1);
			}
		}
		target.tweenjs_count = 0;
	};

	/**
	 * Stop and remove all existing tweens.
	 * @method removeAllTweens
	 * @static
	 * @since 0.4.1
	 */
	Tween.removeAllTweens = function() {
		var tweens = Tween._tweens;
		for (var i= 0, l=tweens.length; i<l; i++) {
			var tween = tweens[i];
			tween._paused = true;
			tween.target&&(tween.target.tweenjs_count = 0);
		}
		tweens.length = 0;
	};

	/**
	 * Indicates whether there are any active tweens (and how many) on the target object (if specified) or in general.
	 * @method hasActiveTweens
	 * @param {Object} [target] The target to check for active tweens. If not specified, the return value will indicate
	 * if there are any active tweens on any target.
	 * @return {Boolean} If there are active tweens.
	 * @static
	 */
	Tween.hasActiveTweens = function(target) {
		if (target) { return target.tweenjs_count != null && !!target.tweenjs_count; }
		return Tween._tweens && !!Tween._tweens.length;
	};

	/**
	 * Installs a plugin, which can modify how certain properties are handled when tweened. See the {{#crossLink "CSSPlugin"}}{{/crossLink}}
	 * for an example of how to write TweenJS plugins.
	 * @method installPlugin
	 * @static
	 * @param {Object} plugin The plugin class to install
	 * @param {Array} properties An array of properties that the plugin will handle.
	 */
	Tween.installPlugin = function(plugin, properties) {
		var priority = plugin.priority;
		if (priority == null) { plugin.priority = priority = 0; }
		for (var i=0,l=properties.length,p=Tween._plugins;i<l;i++) {
			var n = properties[i];
			if (!p[n]) { p[n] = [plugin]; }
			else {
				var arr = p[n];
				for (var j=0,jl=arr.length;j<jl;j++) {
					if (priority < arr[j].priority) { break; }
				}
				p[n].splice(j,0,plugin);
			}
		}
	};

	/**
	 * Registers or unregisters a tween with the ticking system.
	 * @method _register
	 * @param {Tween} tween The tween instance to register or unregister.
	 * @param {Boolean} value If `true`, the tween is registered. If `false` the tween is unregistered.
	 * @static
	 * @protected
	 */
	Tween._register = function(tween, value) {
		var target = tween._target;
		var tweens = Tween._tweens;
		if (value && !tween._registered) {
			// TODO: this approach might fail if a dev is using sealed objects in ES5
			if (target) { target.tweenjs_count = target.tweenjs_count ? target.tweenjs_count+1 : 1; }
			tweens.push(tween);
			if (!Tween._inited && createjs.Ticker) { createjs.Ticker.addEventListener("tick", Tween); Tween._inited = true; }
		} else if (!value && tween._registered) {
			if (target) { target.tweenjs_count--; }
			var i = tweens.length;
			while (i--) {
				if (tweens[i] == tween) {
					tweens.splice(i, 1);
					break;
				}
			}
		}
		tween._registered = value;
	};


// events:
	/**
	 * Called whenever the tween's position changes.
	 * @event change
	 * @since 0.4.0
	 **/
	

// public methods:
	/**
	 * Queues a wait (essentially an empty tween).
	 * <h4>Example</h4>
	 *
	 *		//This tween will wait 1s before alpha is faded to 0.
	 *		createjs.Tween.get(target).wait(1000).to({alpha:0}, 1000);
	 *
	 * @method wait
	 * @param {Number} duration The duration of the wait in milliseconds (or in ticks if `useTicks` is true).
	 * @param {Boolean} [passive] Tween properties will not be updated during a passive wait. This
	 * is mostly useful for use with {{#crossLink "Timeline"}}{{/crossLink}} instances that contain multiple tweens
	 * affecting the same target at different times.
	 * @return {Tween} This tween instance (for chaining calls).
	 **/
	p.wait = function(duration, passive) {
		if (duration == null || duration <= 0) { return this; }
		var o = this._cloneProps(this._curQueueProps);
		return this._addStep({d:duration, p0:o, e:this._linearEase, p1:o, v:passive});
	};

	/**
	 * Queues a tween from the current values to the target properties. Set duration to 0 to jump to these value.
	 * Numeric properties will be tweened from their current value in the tween to the target value. Non-numeric
	 * properties will be set at the end of the specified duration.
	 * <h4>Example</h4>
	 *
	 *		createjs.Tween.get(target).to({alpha:0}, 1000);
	 *
	 * @method to
	 * @param {Object} props An object specifying property target values for this tween (Ex. `{x:300}` would tween the x
	 * property of the target to 300).
	 * @param {Number} [duration=0] The duration of the wait in milliseconds (or in ticks if `useTicks` is true).
	 * @param {Function} [ease="linear"] The easing function to use for this tween. See the {{#crossLink "Ease"}}{{/crossLink}}
	 * class for a list of built-in ease functions.
	 * @return {Tween} This tween instance (for chaining calls).
	 */
	p.to = function(props, duration, ease) {
		if (isNaN(duration) || duration < 0) { duration = 0; }
		return this._addStep({d:duration||0, p0:this._cloneProps(this._curQueueProps), e:ease, p1:this._cloneProps(this._appendQueueProps(props))});
	};

	/**
	 * Queues an action to call the specified function.
	 * <h4>Example</h4>
	 *
	 *   	//would call myFunction() after 1 second.
	 *   	myTween.wait(1000).call(myFunction);
	 *
	 * @method call
	 * @param {Function} callback The function to call.
	 * @param {Array} [params]. The parameters to call the function with. If this is omitted, then the function
	 *      will be called with a single param pointing to this tween.
	 * @param {Object} [scope]. The scope to call the function in. If omitted, it will be called in the target's
	 *      scope.
	 * @return {Tween} This tween instance (for chaining calls).
	 */
	p.call = function(callback, params, scope) {
		return this._addAction({f:callback, p:params ? params : [this], o:scope ? scope : this._target});
	};

	// TODO: add clarification between this and a 0 duration .to:
	/**
	 * Queues an action to set the specified props on the specified target. If target is null, it will use this tween's
	 * target.
	 * <h4>Example</h4>
	 *
	 *		myTween.wait(1000).set({visible:false},foo);
	 *
	 * @method set
	 * @param {Object} props The properties to set (ex. `{visible:false}`).
	 * @param {Object} [target] The target to set the properties on. If omitted, they will be set on the tween's target.
	 * @return {Tween} This tween instance (for chaining calls).
	 */
	p.set = function(props, target) {
		return this._addAction({f:this._set, o:this, p:[props, target ? target : this._target]});
	};

	/**
	 * Queues an action to play (unpause) the specified tween. This enables you to sequence multiple tweens.
	 * <h4>Example</h4>
	 *
	 *		myTween.to({x:100},500).play(otherTween);
	 *
	 * @method play
	 * @param {Tween} tween The tween to play.
	 * @return {Tween} This tween instance (for chaining calls).
	 */
	p.play = function(tween) {
		if (!tween) { tween = this; }
		return this.call(tween.setPaused, [false], tween);
	};

	/**
	 * Queues an action to pause the specified tween.
	 * @method pause
	 * @param {Tween} tween The tween to pause. If null, it pauses this tween.
	 * @return {Tween} This tween instance (for chaining calls)
	 */
	p.pause = function(tween) {
		if (!tween) { tween = this; }
		return this.call(tween.setPaused, [true], tween);
	};

	/**
	 * Advances the tween to a specified position.
	 * @method setPosition
	 * @param {Number} value The position to seek to in milliseconds (or ticks if useTicks is true).
	 * @param {Number} [actionsMode=1] Specifies how actions are handled (ie. call, set, play, pause):
	 * <ul>
	 *      <li>{{#crossLink "Tween/NONE:property"}}{{/crossLink}} (0) - run no actions.</li>
	 *      <li>{{#crossLink "Tween/LOOP:property"}}{{/crossLink}} (1) - if new position is less than old, then run all
	 *      actions between old and duration, then all actions between 0 and new.</li>
	 *      <li>{{#crossLink "Tween/REVERSE:property"}}{{/crossLink}} (2) - if new position is less than old, run all
	 *      actions between them in reverse.</li>
	 * </ul>
	 * @return {Boolean} Returns `true` if the tween is complete (ie. the full tween has run & {{#crossLink "Tween/loop:property"}}{{/crossLink}}
	 * is `false`).
	 */
	p.setPosition = function(value, actionsMode) {
		if (value < 0) { value = 0; }
		if (actionsMode == null) { actionsMode = 1; }

		// normalize position:
		var t = value;
		var end = false;
		if (t >= this.duration) {
			if (this.loop) { t = t%this.duration; }
			else {
				t = this.duration;
				end = true;
			}
		}
		if (t == this._prevPos) { return end; }


		var prevPos = this._prevPos;
		this.position = this._prevPos = t; // set this in advance in case an action modifies position.
		this._prevPosition = value;

		// handle tweens:
		if (this._target) {
			if (end) {
				// addresses problems with an ending zero length step.
				this._updateTargetProps(null,1);
			} else if (this._steps.length > 0) {
				// find our new tween index:
				for (var i=0, l=this._steps.length; i<l; i++) {
					if (this._steps[i].t > t) { break; }
				}
				var step = this._steps[i-1];
				this._updateTargetProps(step,(this._stepPosition = t-step.t)/step.d);
			}
		}

		// run actions:
		if (actionsMode != 0 && this._actions.length > 0) {
			if (this._useTicks) {
				// only run the actions we landed on.
				this._runActions(t,t);
			} else if (actionsMode == 1 && t<prevPos) {
				if (prevPos != this.duration) { this._runActions(prevPos, this.duration); }
				this._runActions(0, t, true);
			} else {
				this._runActions(prevPos, t);
			}
		}

		if (end) { this.setPaused(true); }

        this.dispatchEvent("change");
		return end;
	};

	/**
	 * Advances this tween by the specified amount of time in milliseconds (or ticks if`useTicks` is `true`).
	 * This is normally called automatically by the Tween engine (via {{#crossLink "Tween/tick"}}{{/crossLink}}), but is
	 * exposed for advanced uses.
	 * @method tick
	 * @param {Number} delta The time to advance in milliseconds (or ticks if `useTicks` is `true`).
	 */
	p.tick = function(delta) {
		if (this._paused) { return; }
		this.setPosition(this._prevPosition+delta);
	};

	/**
	 * Pauses or plays this tween.
	 * @method setPaused
	 * @param {Boolean} [value=true] Indicates whether the tween should be paused (`true`) or played (`false`).
	 * @return {Tween} This tween instance (for chaining calls)
	 */
	p.setPaused = function(value) {
		if (this._paused === !!value) { return this; }
		this._paused = !!value;
		Tween._register(this, !value);
		return this;
	};

	// tiny api (primarily for tool output):
	p.w = p.wait;
	p.t = p.to;
	p.c = p.call;
	p.s = p.set;

	/**
	 * Returns a string representation of this object.
	 * @method toString
	 * @return {String} a string representation of the instance.
	 */
	p.toString = function() {
		return "[Tween]";
	};

	/**
	 * @method clone
	 * @protected
	 */
	p.clone = function() {
		throw("Tween can not be cloned.")
	};

// private methods:
	/**
	 * @method _updateTargetProps
	 * @param {Object} step
	 * @param {Number} ratio
	 * @protected
	 */
	p._updateTargetProps = function(step, ratio) {
		var p0,p1,v,v0,v1,arr;
		if (!step && ratio == 1) {
			// GDS: when does this run? Just at the very end? Shouldn't.
			this.passive = false;
			p0 = p1 = this._curQueueProps;
		} else {
			this.passive = !!step.v;
			if (this.passive) { return; } // don't update props.
			// apply ease to ratio.
			if (step.e) { ratio = step.e(ratio,0,1,1); }
			p0 = step.p0;
			p1 = step.p1;
		}

		for (var n in this._initQueueProps) {
			if ((v0 = p0[n]) == null) { p0[n] = v0 = this._initQueueProps[n]; }
			if ((v1 = p1[n]) == null) { p1[n] = v1 = v0; }
			if (v0 == v1 || ratio == 0 || ratio == 1 || (typeof(v0) != "number")) {
				// no interpolation - either at start, end, values don't change, or the value is non-numeric.
				v = ratio == 1 ? v1 : v0;
			} else {
				v = v0+(v1-v0)*ratio;
			}

			var ignore = false;
			if (arr = Tween._plugins[n]) {
				for (var i=0,l=arr.length;i<l;i++) {
					var v2 = arr[i].tween(this, n, v, p0, p1, ratio, !!step&&p0==p1, !step);
					if (v2 == Tween.IGNORE) { ignore = true; }
					else { v = v2; }
				}
			}
			if (!ignore) { this._target[n] = v; }
		}

	};

	/**
	 * @method _runActions
	 * @param {Number} startPos
	 * @param {Number} endPos
	 * @param {Boolean} includeStart
	 * @protected
	 */
	p._runActions = function(startPos, endPos, includeStart) {
		var sPos = startPos;
		var ePos = endPos;
		var i = -1;
		var j = this._actions.length;
		var k = 1;
		if (startPos > endPos) {
			// running backwards, flip everything:
			sPos = endPos;
			ePos = startPos;
			i = j;
			j = k = -1;
		}
		while ((i+=k) != j) {
			var action = this._actions[i];
			var pos = action.t;
			if (pos == ePos || (pos > sPos && pos < ePos) || (includeStart && pos == startPos) ) {
				action.f.apply(action.o, action.p);
			}
		}
	};

	/**
	 * @method _appendQueueProps
	 * @param {Object} o
	 * @protected
	 */
	p._appendQueueProps = function(o) {
		var arr,oldValue,i, l, injectProps;
		for (var n in o) {
			if (this._initQueueProps[n] === undefined) {
				oldValue = this._target[n];

				// init plugins:
				if (arr = Tween._plugins[n]) {
					for (i=0,l=arr.length;i<l;i++) {
						oldValue = arr[i].init(this, n, oldValue);
					}
				}
				this._initQueueProps[n] = this._curQueueProps[n] = (oldValue===undefined) ? null : oldValue;
			} else {
				oldValue = this._curQueueProps[n];
			}
		}

		for (var n in o) {
			oldValue = this._curQueueProps[n];
			if (arr = Tween._plugins[n]) {
				injectProps = injectProps||{};
				for (i=0, l=arr.length;i<l;i++) {
					// TODO: remove the check for .step in the next version. It's here for backwards compatibility.
					if (arr[i].step) { arr[i].step(this, n, oldValue, o[n], injectProps); }
				}
			}
			this._curQueueProps[n] = o[n];
		}
		if (injectProps) { this._appendQueueProps(injectProps); }
		return this._curQueueProps;
	};

	/**
	 * @method _cloneProps
	 * @param {Object} props
	 * @protected
	 */
	p._cloneProps = function(props) {
		var o = {};
		for (var n in props) {
			o[n] = props[n];
		}
		return o;
	};

	/**
	 * @method _addStep
	 * @param {Object} o
	 * @protected
	 */
	p._addStep = function(o) {
		if (o.d > 0) {
			this._steps.push(o);
			o.t = this.duration;
			this.duration += o.d;
		}
		return this;
	};

	/**
	 * @method _addAction
	 * @param {Object} o
	 * @protected
	 */
	p._addAction = function(o) {
		o.t = this.duration;
		this._actions.push(o);
		return this;
	};

	/**
	 * @method _set
	 * @param {Object} props
	 * @param {Object} o
	 * @protected
	 */
	p._set = function(props, o) {
		for (var n in props) {
			o[n] = props[n];
		}
	};

	createjs.Tween = createjs.promote(Tween, "EventDispatcher");

}());

//##############################################################################
// Timeline.js
//##############################################################################

(function() {
	"use strict";
	

// constructor	
	/**
	 * The Timeline class synchronizes multiple tweens and allows them to be controlled as a group. Please note that if a
	 * timeline is looping, the tweens on it may appear to loop even if the "loop" property of the tween is false.
	 * @class Timeline
	 * @param {Array} tweens An array of Tweens to add to this timeline. See {{#crossLink "Timeline/addTween"}}{{/crossLink}}
	 * for more info.
	 * @param {Object} labels An object defining labels for using {{#crossLink "Timeline/gotoAndPlay"}}{{/crossLink}}/{{#crossLink "Timeline/gotoAndStop"}}{{/crossLink}}.
	 * See {{#crossLink "Timeline/setLabels"}}{{/crossLink}}
	 * for details.
	 * @param {Object} props The configuration properties to apply to this tween instance (ex. `{loop:true}`). All properties
	 * default to false. Supported props are:<UL>
	 *    <LI> loop: sets the loop property on this tween.</LI>
	 *    <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI>
	 *    <LI> ignoreGlobalPause: sets the ignoreGlobalPause property on this tween.</LI>
	 *    <LI> paused: indicates whether to start the tween paused.</LI>
	 *    <LI> position: indicates the initial position for this timeline.</LI>
	 *    <LI> onChange: specifies a listener to add for the {{#crossLink "Timeline/change:event"}}{{/crossLink}} event.</LI>
	 * </UL>
	 * @extends EventDispatcher
	 * @constructor
	 **/
	function Timeline(tweens, labels, props) {
		this.EventDispatcher_constructor();

	// public properties:
		/**
		 * Causes this timeline to continue playing when a global pause is active.
		 * @property ignoreGlobalPause
		 * @type Boolean
		 **/
		this.ignoreGlobalPause = false;

		/**
		 * The total duration of this timeline in milliseconds (or ticks if `useTicks `is `true`). This value is usually
		 * automatically updated as you modify the timeline. See {{#crossLink "Timeline/updateDuration"}}{{/crossLink}}
		 * for more information.
		 * @property duration
		 * @type Number
		 * @default 0
		 * @readonly
		 **/
		this.duration = 0;

		/**
		 * If true, the timeline will loop when it reaches the end. Can be set via the props param.
		 * @property loop
		 * @type Boolean
		 **/
		this.loop = false;

		/**
		 * The current normalized position of the timeline. This will always be a value between 0 and
		 * {{#crossLink "Timeline/duration:property"}}{{/crossLink}}.
		 * Changing this property directly will have no effect.
		 * @property position
		 * @type Object
		 * @readonly
		 **/
		this.position = null;

		// private properties:
		/**
		 * @property _paused
		 * @type Boolean
		 * @protected
		 **/
		this._paused = false;

		/**
		 * @property _tweens
		 * @type Array[Tween]
		 * @protected
		 **/
		this._tweens = [];

		/**
		 * @property _labels
		 * @type Object
		 * @protected
		 **/
		this._labels = null;

		/**
		 * @property _labelList
		 * @type Array[Object]
		 * @protected
		 **/
		this._labelList = null;

		/**
		 * @property _prevPosition
		 * @type Number
		 * @default 0
		 * @protected
		 **/
		this._prevPosition = 0;

		/**
		 * @property _prevPos
		 * @type Number
		 * @default -1
		 * @protected
		 **/
		this._prevPos = -1;

		/**
		 * @property _useTicks
		 * @type Boolean
		 * @default false
		 * @protected
		 **/
		this._useTicks = false;
		
		/**
		 * Indicates whether the timeline is currently registered with Tween.
		 * @property _registered
		 * @type {boolean}
		 * @default false
		 * @protected
		 */
		this._registered = false;


		if (props) {
			this._useTicks = props.useTicks;
			this.loop = props.loop;
			this.ignoreGlobalPause = props.ignoreGlobalPause;
			props.onChange&&this.addEventListener("change", props.onChange);
		}
		if (tweens) { this.addTween.apply(this, tweens); }
		this.setLabels(labels);
		if (props&&props.paused) { this._paused=true; }
		else { createjs.Tween._register(this,true); }
		if (props&&props.position!=null) { this.setPosition(props.position, createjs.Tween.NONE); }
		
	};
	
	var p = createjs.extend(Timeline, createjs.EventDispatcher);

	// TODO: deprecated
	// p.initialize = function() {}; // searchable for devs wondering where it is. REMOVED. See docs for details.

	
// events:
	/**
	 * Called whenever the timeline's position changes.
	 * @event change
	 * @since 0.5.0
	 **/


// public methods:
	/**
	 * Adds one or more tweens (or timelines) to this timeline. The tweens will be paused (to remove them from the
	 * normal ticking system) and managed by this timeline. Adding a tween to multiple timelines will result in
	 * unexpected behaviour.
	 * @method addTween
	 * @param {Tween} ...tween The tween(s) to add. Accepts multiple arguments.
	 * @return {Tween} The first tween that was passed in.
	 **/
	p.addTween = function(tween) {
		var l = arguments.length;
		if (l > 1) {
			for (var i=0; i<l; i++) { this.addTween(arguments[i]); }
			return arguments[0];
		} else if (l == 0) { return null; }
		this.removeTween(tween);
		this._tweens.push(tween);
		tween.setPaused(true);
		tween._paused = false;
		tween._useTicks = this._useTicks;
		if (tween.duration > this.duration) { this.duration = tween.duration; }
		if (this._prevPos >= 0) { tween.setPosition(this._prevPos, createjs.Tween.NONE); }
		return tween;
	};

	/**
	 * Removes one or more tweens from this timeline.
	 * @method removeTween
	 * @param {Tween} ...tween The tween(s) to remove. Accepts multiple arguments.
	 * @return Boolean Returns `true` if all of the tweens were successfully removed.
	 **/
	p.removeTween = function(tween) {
		var l = arguments.length;
		if (l > 1) {
			var good = true;
			for (var i=0; i<l; i++) { good = good && this.removeTween(arguments[i]); }
			return good;
		} else if (l == 0) { return false; }

		var tweens = this._tweens;
		var i = tweens.length;
		while (i--) {
			if (tweens[i] == tween) {
				tweens.splice(i, 1);
				if (tween.duration >= this.duration) { this.updateDuration(); }
				return true;
			}
		}
		return false;
	};

	/**
	 * Adds a label that can be used with {{#crossLink "Timeline/gotoAndPlay"}}{{/crossLink}}/{{#crossLink "Timeline/gotoAndStop"}}{{/crossLink}}.
	 * @method addLabel
	 * @param {String} label The label name.
	 * @param {Number} position The position this label represents.
	 **/
	p.addLabel = function(label, position) {
		this._labels[label] = position;
		var list = this._labelList;
		if (list) {
			for (var i= 0,l=list.length; i<l; i++) { if (position < list[i].position) { break; } }
			list.splice(i, 0, {label:label, position:position});
		}
	};

	/**
	 * Defines labels for use with gotoAndPlay/Stop. Overwrites any previously set labels.
	 * @method setLabels
	 * @param {Object} o An object defining labels for using {{#crossLink "Timeline/gotoAndPlay"}}{{/crossLink}}/{{#crossLink "Timeline/gotoAndStop"}}{{/crossLink}}
	 * in the form `{labelName:time}` where time is in milliseconds (or ticks if `useTicks` is `true`).
	 **/
	p.setLabels = function(o) {
		this._labels = o ?  o : {};
	};

	/**
	 * Returns a sorted list of the labels defined on this timeline.
	 * @method getLabels
	 * @return {Array[Object]} A sorted array of objects with label and position properties.
	 **/
	p.getLabels = function() {
		var list = this._labelList;
		if (!list) {
			list = this._labelList = [];
			var labels = this._labels;
			for (var n in labels) {
				list.push({label:n, position:labels[n]});
			}
			list.sort(function (a,b) { return a.position- b.position; });
		}
		return list;
	};

	/**
	 * Returns the name of the label on or immediately before the current position. For example, given a timeline with
	 * two labels, "first" on frame index 4, and "second" on frame 8, getCurrentLabel would return:
	 * <UL>
	 * 		<LI>null if the current position is 2.</LI>
	 * 		<LI>"first" if the current position is 4.</LI>
	 * 		<LI>"first" if the current position is 7.</LI>
	 * 		<LI>"second" if the current position is 15.</LI>
	 * </UL>
	 * @method getCurrentLabel
	 * @return {String} The name of the current label or null if there is no label
	 **/
	p.getCurrentLabel = function() {
		var labels = this.getLabels();
		var pos = this.position;
		var l = labels.length;
		if (l) {
			for (var i = 0; i<l; i++) { if (pos < labels[i].position) { break; } }
			return (i==0) ? null : labels[i-1].label;
		}
		return null;
	};

	/**
	 * Unpauses this timeline and jumps to the specified position or label.
	 * @method gotoAndPlay
	 * @param {String|Number} positionOrLabel The position in milliseconds (or ticks if `useTicks` is `true`)
	 * or label to jump to.
	 **/
	p.gotoAndPlay = function(positionOrLabel) {
		this.setPaused(false);
		this._goto(positionOrLabel);
	};

	/**
	 * Pauses this timeline and jumps to the specified position or label.
	 * @method gotoAndStop
	 * @param {String|Number} positionOrLabel The position in milliseconds (or ticks if `useTicks` is `true`) or label
	 * to jump to.
	 **/
	p.gotoAndStop = function(positionOrLabel) {
		this.setPaused(true);
		this._goto(positionOrLabel);
	};

	/**
	 * Advances the timeline to the specified position.
	 * @method setPosition
	 * @param {Number} value The position to seek to in milliseconds (or ticks if `useTicks` is `true`).
	 * @param {Number} [actionsMode] parameter specifying how actions are handled. See the Tween {{#crossLink "Tween/setPosition"}}{{/crossLink}}
	 * method for more details.
	 * @return {Boolean} Returns `true` if the timeline is complete (ie. the full timeline has run & {{#crossLink "Timeline/loop:property"}}{{/crossLink}}
	 * is `false`).
	 **/
	p.setPosition = function(value, actionsMode) {
		var t = this._calcPosition(value);
		var end = !this.loop && value >= this.duration;
		if (t == this._prevPos) { return end; }
		this._prevPosition = value;
		this.position = this._prevPos = t; // in case an action changes the current frame.
		for (var i=0, l=this._tweens.length; i<l; i++) {
			this._tweens[i].setPosition(t, actionsMode);
			if (t != this._prevPos) { return false; } // an action changed this timeline's position.
		}
		if (end) { this.setPaused(true); }
		this.dispatchEvent("change");
		return end;
	};

	/**
	 * Pauses or plays this timeline.
	 * @method setPaused
	 * @param {Boolean} value Indicates whether the tween should be paused (`true`) or played (`false`).
	 **/
	p.setPaused = function(value) {
		this._paused = !!value; 
		createjs.Tween._register(this, !value);
	};

	/**
	 * Recalculates the duration of the timeline. The duration is automatically updated when tweens are added or removed,
	 * but this method is useful if you modify a tween after it was added to the timeline.
	 * @method updateDuration
	 **/
	p.updateDuration = function() {
		this.duration = 0;
		for (var i=0,l=this._tweens.length; i<l; i++) {
			var tween = this._tweens[i];
			if (tween.duration > this.duration) { this.duration = tween.duration; }
		}
	};

	/**
	 * Advances this timeline by the specified amount of time in milliseconds (or ticks if `useTicks` is `true`).
	 * This is normally called automatically by the Tween engine (via the {{#crossLink "Tween/tick:event"}}{{/crossLink}}
	 * event), but is exposed for advanced uses.
	 * @method tick
	 * @param {Number} delta The time to advance in milliseconds (or ticks if useTicks is true).
	 **/
	p.tick = function(delta) {
		this.setPosition(this._prevPosition+delta);
	};

	/**
	 * If a numeric position is passed, it is returned unchanged. If a string is passed, the position of the
	 * corresponding frame label will be returned, or `null` if a matching label is not defined.
	 * @method resolve
	 * @param {String|Number} positionOrLabel A numeric position value or label string.
	 **/
	p.resolve = function(positionOrLabel) {
		var pos = Number(positionOrLabel);
		if (isNaN(pos)) { pos = this._labels[positionOrLabel]; }
		return pos;
	};

	/**
	* Returns a string representation of this object.
	* @method toString
	* @return {String} a string representation of the instance.
	**/
	p.toString = function() {
		return "[Timeline]";
	};

	/**
	 * @method clone
	 * @protected
	 **/
	p.clone = function() {
		throw("Timeline can not be cloned.")
	};

// private methods:
	/**
	 * @method _goto
	 * @param {String | Number} positionOrLabel
	 * @protected
	 **/
	p._goto = function(positionOrLabel) {
		var pos = this.resolve(positionOrLabel);
		if (pos != null) { this.setPosition(pos); }
	};
	
	/**
	 * @method _calcPosition
	 * @param {Number} value
	 * @return {Number}
	 * @protected
	 **/
	p._calcPosition = function(value) {
		if (value < 0) { return 0; }
		if (value < this.duration) { return value; }
		return this.loop ? value%this.duration : this.duration;
	};

	createjs.Timeline = createjs.promote(Timeline, "EventDispatcher");

}());

//##############################################################################
// Ease.js
//##############################################################################

(function() {
	"use strict";

	/**
	 * The Ease class provides a collection of easing functions for use with TweenJS. It does not use the standard 4 param
	 * easing signature. Instead it uses a single param which indicates the current linear ratio (0 to 1) of the tween.
	 *
	 * Most methods on Ease can be passed directly as easing functions:
	 *
	 *      Tween.get(target).to({x:100}, 500, Ease.linear);
	 *
	 * However, methods beginning with "get" will return an easing function based on parameter values:
	 *
	 *      Tween.get(target).to({y:200}, 500, Ease.getPowIn(2.2));
	 *
	 * Please see the <a href="http://www.createjs.com/Demos/TweenJS/Tween_SparkTable">spark table demo</a> for an
	 * overview of the different ease types on <a href="http://tweenjs.com">TweenJS.com</a>.
	 *
	 * <em>Equations derived from work by Robert Penner.</em>
	 * @class Ease
	 * @static
	 **/
	function Ease() {
		throw "Ease cannot be instantiated.";
	}


// static methods and properties
	/**
	 * @method linear
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.linear = function(t) { return t; };

	/**
	 * Identical to linear.
	 * @method none
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.none = Ease.linear;

	/**
	 * Mimics the simple -100 to 100 easing in Flash Pro.
	 * @method get
	 * @param {Number} amount A value from -1 (ease in) to 1 (ease out) indicating the strength and direction of the ease.
	 * @static
	 * @return {Function}
	 **/
	Ease.get = function(amount) {
		if (amount < -1) { amount = -1; }
		if (amount > 1) { amount = 1; }
		return function(t) {
			if (amount==0) { return t; }
			if (amount<0) { return t*(t*-amount+1+amount); }
			return t*((2-t)*amount+(1-amount));
		};
	};

	/**
	 * Configurable exponential ease.
	 * @method getPowIn
	 * @param {Number} pow The exponent to use (ex. 3 would return a cubic ease).
	 * @static
	 * @return {Function}
	 **/
	Ease.getPowIn = function(pow) {
		return function(t) {
			return Math.pow(t,pow);
		};
	};

	/**
	 * Configurable exponential ease.
	 * @method getPowOut
	 * @param {Number} pow The exponent to use (ex. 3 would return a cubic ease).
	 * @static
	 * @return {Function}
	 **/
	Ease.getPowOut = function(pow) {
		return function(t) {
			return 1-Math.pow(1-t,pow);
		};
	};

	/**
	 * Configurable exponential ease.
	 * @method getPowInOut
	 * @param {Number} pow The exponent to use (ex. 3 would return a cubic ease).
	 * @static
	 * @return {Function}
	 **/
	Ease.getPowInOut = function(pow) {
		return function(t) {
			if ((t*=2)<1) return 0.5*Math.pow(t,pow);
			return 1-0.5*Math.abs(Math.pow(2-t,pow));
		};
	};

	/**
	 * @method quadIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quadIn = Ease.getPowIn(2);
	/**
	 * @method quadOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quadOut = Ease.getPowOut(2);
	/**
	 * @method quadInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quadInOut = Ease.getPowInOut(2);

	/**
	 * @method cubicIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.cubicIn = Ease.getPowIn(3);
	/**
	 * @method cubicOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.cubicOut = Ease.getPowOut(3);
	/**
	 * @method cubicInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.cubicInOut = Ease.getPowInOut(3);

	/**
	 * @method quartIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quartIn = Ease.getPowIn(4);
	/**
	 * @method quartOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quartOut = Ease.getPowOut(4);
	/**
	 * @method quartInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quartInOut = Ease.getPowInOut(4);

	/**
	 * @method quintIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quintIn = Ease.getPowIn(5);
	/**
	 * @method quintOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quintOut = Ease.getPowOut(5);
	/**
	 * @method quintInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.quintInOut = Ease.getPowInOut(5);

	/**
	 * @method sineIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.sineIn = function(t) {
		return 1-Math.cos(t*Math.PI/2);
	};

	/**
	 * @method sineOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.sineOut = function(t) {
		return Math.sin(t*Math.PI/2);
	};

	/**
	 * @method sineInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.sineInOut = function(t) {
		return -0.5*(Math.cos(Math.PI*t) - 1);
	};

	/**
	 * Configurable "back in" ease.
	 * @method getBackIn
	 * @param {Number} amount The strength of the ease.
	 * @static
	 * @return {Function}
	 **/
	Ease.getBackIn = function(amount) {
		return function(t) {
			return t*t*((amount+1)*t-amount);
		};
	};
	/**
	 * @method backIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.backIn = Ease.getBackIn(1.7);

	/**
	 * Configurable "back out" ease.
	 * @method getBackOut
	 * @param {Number} amount The strength of the ease.
	 * @static
	 * @return {Function}
	 **/
	Ease.getBackOut = function(amount) {
		return function(t) {
			return (--t*t*((amount+1)*t + amount) + 1);
		};
	};
	/**
	 * @method backOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.backOut = Ease.getBackOut(1.7);

	/**
	 * Configurable "back in out" ease.
	 * @method getBackInOut
	 * @param {Number} amount The strength of the ease.
	 * @static
	 * @return {Function}
	 **/
	Ease.getBackInOut = function(amount) {
		amount*=1.525;
		return function(t) {
			if ((t*=2)<1) return 0.5*(t*t*((amount+1)*t-amount));
			return 0.5*((t-=2)*t*((amount+1)*t+amount)+2);
		};
	};
	/**
	 * @method backInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.backInOut = Ease.getBackInOut(1.7);

	/**
	 * @method circIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.circIn = function(t) {
		return -(Math.sqrt(1-t*t)- 1);
	};

	/**
	 * @method circOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.circOut = function(t) {
		return Math.sqrt(1-(--t)*t);
	};

	/**
	 * @method circInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.circInOut = function(t) {
		if ((t*=2) < 1) return -0.5*(Math.sqrt(1-t*t)-1);
		return 0.5*(Math.sqrt(1-(t-=2)*t)+1);
	};

	/**
	 * @method bounceIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.bounceIn = function(t) {
		return 1-Ease.bounceOut(1-t);
	};

	/**
	 * @method bounceOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.bounceOut = function(t) {
		if (t < 1/2.75) {
			return (7.5625*t*t);
		} else if (t < 2/2.75) {
			return (7.5625*(t-=1.5/2.75)*t+0.75);
		} else if (t < 2.5/2.75) {
			return (7.5625*(t-=2.25/2.75)*t+0.9375);
		} else {
			return (7.5625*(t-=2.625/2.75)*t +0.984375);
		}
	};

	/**
	 * @method bounceInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.bounceInOut = function(t) {
		if (t<0.5) return Ease.bounceIn (t*2) * .5;
		return Ease.bounceOut(t*2-1)*0.5+0.5;
	};

	/**
	 * Configurable elastic ease.
	 * @method getElasticIn
	 * @param {Number} amplitude
	 * @param {Number} period
	 * @static
	 * @return {Function}
	 **/
	Ease.getElasticIn = function(amplitude,period) {
		var pi2 = Math.PI*2;
		return function(t) {
			if (t==0 || t==1) return t;
			var s = period/pi2*Math.asin(1/amplitude);
			return -(amplitude*Math.pow(2,10*(t-=1))*Math.sin((t-s)*pi2/period));
		};
	};
	/**
	 * @method elasticIn
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.elasticIn = Ease.getElasticIn(1,0.3);

	/**
	 * Configurable elastic ease.
	 * @method getElasticOut
	 * @param {Number} amplitude
	 * @param {Number} period
	 * @static
	 * @return {Function}
	 **/
	Ease.getElasticOut = function(amplitude,period) {
		var pi2 = Math.PI*2;
		return function(t) {
			if (t==0 || t==1) return t;
			var s = period/pi2 * Math.asin(1/amplitude);
			return (amplitude*Math.pow(2,-10*t)*Math.sin((t-s)*pi2/period )+1);
		};
	};
	/**
	 * @method elasticOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.elasticOut = Ease.getElasticOut(1,0.3);

	/**
	 * Configurable elastic ease.
	 * @method getElasticInOut
	 * @param {Number} amplitude
	 * @param {Number} period
	 * @static
	 * @return {Function}
	 **/
	Ease.getElasticInOut = function(amplitude,period) {
		var pi2 = Math.PI*2;
		return function(t) {
			var s = period/pi2 * Math.asin(1/amplitude);
			if ((t*=2)<1) return -0.5*(amplitude*Math.pow(2,10*(t-=1))*Math.sin( (t-s)*pi2/period ));
			return amplitude*Math.pow(2,-10*(t-=1))*Math.sin((t-s)*pi2/period)*0.5+1;
		};
	};
	/**
	 * @method elasticInOut
	 * @param {Number} t
	 * @static
	 * @return {Number}
	 **/
	Ease.elasticInOut = Ease.getElasticInOut(1,0.3*1.5);

	createjs.Ease = Ease;

}());

//##############################################################################
// MotionGuidePlugin.js
//##############################################################################

(function() {
	"use strict";

	/**
	 * A TweenJS plugin for working with motion guides.
	 *
	 * To use, install the plugin after TweenJS has loaded. Next tween the 'guide' property with an object as detailed below.
	 *
	 *       createjs.MotionGuidePlugin.install();
	 *
	 * <h4>Example</h4>
	 *
	 *      // Using a Motion Guide
	 *	    createjs.Tween.get(target).to({guide:{ path:[0,0, 0,200,200,200, 200,0,0,0] }},7000);
	 *	    // Visualizing the line
	 *	    graphics.moveTo(0,0).curveTo(0,200,200,200).curveTo(200,0,0,0);
	 *
	 * Each path needs pre-computation to ensure there's fast performance. Because of the pre-computation there's no
	 * built in support for path changes mid tween. These are the Guide Object's properties:<UL>
	 *      <LI> path: Required, Array : The x/y points used to draw the path with a moveTo and 1 to n curveTo calls.</LI>
	 *      <LI> start: Optional, 0-1 : Initial position, default 0 except for when continuing along the same path.</LI>
	 *      <LI> end: Optional, 0-1 : Final position, default 1 if not specified.</LI>
	 *      <LI> orient: Optional, string : "fixed"/"auto"/"cw"/"ccw"<UL>
	 *				<LI>"fixed" forces the object to face down the path all movement (relative to start rotation),</LI>
	 *      		<LI>"auto" rotates the object along the path relative to the line.</LI>
	 *      		<LI>"cw"/"ccw" force clockwise or counter clockwise rotations including flash like behaviour</LI>
	 * 		</UL></LI>
	 * </UL>
	 * Guide objects should not be shared between tweens even if all properties are identical, the library stores
	 * information on these objects in the background and sharing them can cause unexpected behaviour. Values
	 * outside 0-1 range of tweens will be a "best guess" from the appropriate part of the defined curve.
	 *
	 * @class MotionGuidePlugin
	 * @constructor
	 **/
	function MotionGuidePlugin() {
		throw("MotionGuidePlugin cannot be instantiated.")
	};


// static properties:
	/**
	 * @property priority
	 * @protected
	 * @static
	 **/
	MotionGuidePlugin.priority = 0; // high priority, should run sooner

	/**
	 * @property temporary variable storage
	 * @private
	 * @static
	 */
	MotionGuidePlugin._rotOffS;
	/**
	 * @property temporary variable storage
	 * @private
	 * @static
	 */
	MotionGuidePlugin._rotOffE;
	/**
	 * @property temporary variable storage
	 * @private
	 * @static
	 */
	MotionGuidePlugin._rotNormS;
	/**
	 * @property temporary variable storage
	 * @private
	 * @static
	 */
	MotionGuidePlugin._rotNormE;


// static methods
	/**
	 * Installs this plugin for use with TweenJS. Call this once after TweenJS is loaded to enable this plugin.
	 * @method install
	 * @static
	 **/
	MotionGuidePlugin.install = function() {
		createjs.Tween.installPlugin(MotionGuidePlugin, ["guide", "x", "y", "rotation"]);
		return createjs.Tween.IGNORE;
	};

	/**
	 * @method init
	 * @protected
	 * @static
	 **/
	MotionGuidePlugin.init = function(tween, prop, value) {
		var target = tween.target;
		if(!target.hasOwnProperty("x")){ target.x = 0; }
		if(!target.hasOwnProperty("y")){ target.y = 0; }
		if(!target.hasOwnProperty("rotation")){ target.rotation = 0; }

		if(prop=="rotation"){ tween.__needsRot = true; }
		return prop=="guide"?null:value;
	};

	/**
	 * @method step
	 * @protected
	 * @static
	 **/
	MotionGuidePlugin.step = function(tween, prop, startValue, endValue, injectProps) {
		// other props
		if(prop == "rotation"){
			tween.__rotGlobalS = startValue;
			tween.__rotGlobalE = endValue;
			MotionGuidePlugin.testRotData(tween, injectProps);
		}
		if(prop != "guide"){ return endValue; }

		// guide only information - Start -
		var temp, data = endValue;
		if(!data.hasOwnProperty("path")){ data.path = []; }
		var path = data.path;
		if(!data.hasOwnProperty("end")){ data.end = 1; }
		if(!data.hasOwnProperty("start")){
			data.start = (startValue&&startValue.hasOwnProperty("end")&&startValue.path===path)?startValue.end:0;
		}

		// Figure out subline information
		if(data.hasOwnProperty("_segments") && data._length){ return endValue; }
		var l = path.length;
		var accuracy = 10;		// Adjust to improve line following precision but sacrifice performance (# of seg)
		if(l >= 6 && (l-2) % 4 == 0){	// Enough points && contains correct number per entry ignoring start
			data._segments = [];
			data._length = 0;
			for(var i=2; i<l; i+=4){
				var sx = path[i-2], sy = path[i-1];
				var cx = path[i+0], cy = path[i+1];
				var ex = path[i+2], ey = path[i+3];
				var oldX = sx, oldY = sy;
				var tempX, tempY, total = 0;
				var sublines = [];
				for(var j=1; j<=accuracy; j++){
					var t = j/accuracy;
					var inv = 1 - t;
					tempX = inv*inv * sx + 2 * inv * t * cx + t*t * ex;
					tempY = inv*inv * sy + 2 * inv * t * cy + t*t * ey;
					total += sublines[sublines.push(Math.sqrt((temp=tempX-oldX)*temp + (temp=tempY-oldY)*temp))-1];
					oldX = tempX;
					oldY = tempY;
				}
				data._segments.push(total);
				data._segments.push(sublines);
				data._length += total;
			}
		} else {
			throw("invalid 'path' data, please see documentation for valid paths");
		}

		// Setup x/y tweens
		temp = data.orient;
		data.orient = true;
		var o = {};
		MotionGuidePlugin.calc(data, data.start, o);
		tween.__rotPathS = Number(o.rotation.toFixed(5));
		MotionGuidePlugin.calc(data, data.end, o);
		tween.__rotPathE = Number(o.rotation.toFixed(5));
		data.orient = false;	//here and now we don't know if we need to
		MotionGuidePlugin.calc(data, data.end, injectProps);
		data.orient = temp;

		// Setup rotation properties
		if(!data.orient){ return endValue; }
		tween.__guideData = data;
		MotionGuidePlugin.testRotData(tween, injectProps);
		return endValue;
	};

	/**
	 * @method testRotData
	 * @protected
	 * @static
	 **/
	MotionGuidePlugin.testRotData = function(tween, injectProps){

		// no rotation informat? if we need it come back, if we don't use 0 & ensure we have guide data
		if(tween.__rotGlobalS === undefined || tween.__rotGlobalE === undefined){
			if(tween.__needsRot){ return; }
			if(tween._curQueueProps.rotation !== undefined){
				tween.__rotGlobalS = tween.__rotGlobalE = tween._curQueueProps.rotation;
			} else {
				tween.__rotGlobalS = tween.__rotGlobalE = injectProps.rotation = tween.target.rotation || 0;
			}
		}
		if(tween.__guideData === undefined){ return; }

		// Process rotation properties
		var data = tween.__guideData;
		var rotGlobalD = tween.__rotGlobalE - tween.__rotGlobalS;
		var rotPathD = tween.__rotPathE - tween.__rotPathS;
		var rot = rotGlobalD - rotPathD;

		if(data.orient == "auto"){
			if(rot > 180){			rot -= 360; }
			else if(rot < -180){	rot += 360; }

		} else if(data.orient == "cw"){
			while(rot < 0){ rot += 360; }
			if(rot == 0 && rotGlobalD > 0 && rotGlobalD != 180){ rot += 360; }

		} else if(data.orient == "ccw"){
			rot = rotGlobalD - ((rotPathD > 180)?(360-rotPathD):(rotPathD));	// sign flipping on path
			while(rot > 0){ rot -= 360; }
			if(rot == 0 && rotGlobalD < 0 && rotGlobalD != -180){ rot -= 360; }
		}

		data.rotDelta = rot;
		data.rotOffS = tween.__rotGlobalS - tween.__rotPathS;

		// reset
		tween.__rotGlobalS = tween.__rotGlobalE = tween.__guideData = tween.__needsRot = undefined;
	};

	/**
	 * @method tween
	 * @protected
	 * @static
	 **/
	MotionGuidePlugin.tween = function(tween, prop, value, startValues, endValues, ratio, wait, end) {
		var data = endValues.guide;
		if(data == undefined || data === startValues.guide){ return value; }
		if(data.lastRatio != ratio){
			// first time through so calculate what I need to
			var t = ((data.end-data.start)*(wait?data.end:ratio)+data.start);
			MotionGuidePlugin.calc(data, t, tween.target);
			switch(data.orient){
				case "cw":		// mix in the original rotation
				case "ccw":
				case "auto": tween.target.rotation += data.rotOffS + data.rotDelta*ratio; break;
				case "fixed":	// follow fixed behaviour to solve potential issues
				default: tween.target.rotation += data.rotOffS; break;
			}
			data.lastRatio = ratio;
		}
		if(prop == "rotation" && ((!data.orient) || data.orient == "false")){ return value; }
		return tween.target[prop];
	};

	/**
	 * Determine the appropriate x/y/rotation information about a path for a given ratio along the path.
	 * Assumes a path object with all optional parameters specified.
	 * @param data Data object you would pass to the "guide:" property in a Tween
	 * @param ratio 0-1 Distance along path, values outside 0-1 are "best guess"
	 * @param target Object to copy the results onto, will use a new object if not supplied.
	 * @return {Object} The target object or a new object w/ the tweened properties
	 * @static
	 */
	MotionGuidePlugin.calc = function(data, ratio, target) {
		if(data._segments == undefined){ throw("Missing critical pre-calculated information, please file a bug"); }
		if(target == undefined){ target = {x:0, y:0, rotation:0}; }
		var seg = data._segments;
		var path = data.path;

		// find segment
		var pos = data._length * ratio;
		var cap = seg.length - 2;
		var n = 0;
		while(pos > seg[n] && n < cap){
			pos -= seg[n];
			n+=2;
		}

		// find subline
		var sublines = seg[n+1];
		var i = 0;
		cap = sublines.length-1;
		while(pos > sublines[i] && i < cap){
			pos -= sublines[i];
			i++;
		}
		var t = (i/++cap)+(pos/(cap*sublines[i]));

		// find x/y
		n = (n*2)+2;
		var inv = 1 - t;
		target.x = inv*inv * path[n-2] + 2 * inv * t * path[n+0] + t*t * path[n+2];
		target.y = inv*inv * path[n-1] + 2 * inv * t * path[n+1] + t*t * path[n+3];

		// orientation
		if(data.orient){
			target.rotation = 57.2957795 * Math.atan2(
				(path[n+1]-path[n-1])*inv + (path[n+3]-path[n+1])*t,
				(path[n+0]-path[n-2])*inv + (path[n+2]-path[n+0])*t);
		}

		return target;
	};

	createjs.MotionGuidePlugin = MotionGuidePlugin;

}());

//##############################################################################
// version.js
//##############################################################################

(function() {
	"use strict";

	/**
	 * Static class holding library specific information such as the version and buildDate of
	 * the library.
	 * @class TweenJS
	 **/
	var s = createjs.TweenJS = createjs.TweenJS || {};

	/**
	 * The version string for this release.
	 * @property version
	 * @type String
	 * @static
	 **/
	s.version = /*=version*/"0.6.2"; // injected by build process

	/**
	 * The build date for this release in UTC format.
	 * @property buildDate
	 * @type String
	 * @static
	 **/
	s.buildDate = /*=date*/"Thu, 26 Nov 2015 20:44:31 GMT"; // injected by build process

})();;
var cbrnSplash = (function (cjs, an) {

    var p; // shortcut to reference prototypes
    var lib = {}; var ss = {}; var img = {};
    lib.ssMetadata = [
        { name: "cbrnSplash_atlas_", frames: [[842, 2702, 91, 92], [842, 2888, 89, 89], [842, 2796, 90, 90], [842, 2610, 99, 90], [0, 0, 1445, 964], [0, 1928, 1022, 680], [0, 2610, 840, 452], [0, 966, 1211, 960]] }
    ];


    // symbols:
    if (!String.prototype.includes) {
        String.prototype.includes = function (search, start) {
            'use strict';
            if (typeof start !== 'number') {
                start = 0;
            }
            if (start + search.length > this.length) {
                return false;
            } else {
                return this.indexOf(search, start) !== -1;
            }
        };
    }
    if (!Array.prototype.forEach) {
        Array.prototype.forEach = function (callback/*, thisArg*/) {
            var T, k;
            if (this === null) {
                throw new TypeError('this is null or not defined');
            }
            var O = Object(this);
            var len = O.length >>> 0;
            if (typeof callback !== 'function') {
                throw new TypeError(callback + ' is not a function');
            }
            if (arguments.length > 1) {
                T = arguments[1];
            }
            k = 0;
            while (k < len) {
                var kValue;
                if (k in O) {
                    kValue = O[k];
                    callback.call(T, kValue, k, O);
                }
                k++;
            }
        };
    }

    var canvas, stage, exportRoot, anim_container, dom_overlay_container, fnStartAnimation,
        handleFileLoad = function (evt, comp) {
            var images = comp.getImages();
            if (evt && (evt.item.type == "image")) {
                images[evt.item.id] = evt.result;
            }
        },
        handleComplete = function (evt, comp) {
            //This function is always called, irrespective of the content. You can use the variable "stage" after it is created in token create_stage.
            var lib = comp.getLibrary();
            var ss = comp.getSpriteSheet();
            var queue = evt.target;
            var ssMetadata = lib.ssMetadata;
            for (i = 0; i < ssMetadata.length; i++) {
                ss[ssMetadata[i].name] = new createjs.SpriteSheet({ "images": [queue.getResult(ssMetadata[i].name)], "frames": ssMetadata[i].frames })
            }
            var preloaderDiv = document.getElementById("_preload_div_");
            preloaderDiv.style.display = 'none';
            canvas.style.display = 'block';
            exportRoot = new lib.RECOVER_cbrnSplashv12();
            stage = new lib.Stage(canvas);
            stage.enableMouseOver();
            //Registers the "tick" event listener.
            fnStartAnimation = function () {
                stage.addChild(exportRoot);
                createjs.Ticker.setFPS(lib.properties.fps);
                createjs.Ticker.addEventListener("tick", stage);
            }
            //Code to support hidpi screens and responsive scaling.
            an.makeResponsive(true, 'width', true, 1, [canvas, preloaderDiv, anim_container, dom_overlay_container]);
            an.compositionLoaded(lib.properties.id);
            fnStartAnimation();
        },
        init = function () {
            canvas = document.getElementById("canvas");
            anim_container = document.getElementById("animation_container");
            dom_overlay_container = document.getElementById("dom_overlay_container");
            var comp = an.getComposition("F39C5262F2C66D44A33E6756DA740E84");
            var lib = comp.getLibrary();
            var loader = new createjs.LoadQueue(false);
            loader.addEventListener("fileload", function (evt) { handleFileLoad(evt, comp) });
            loader.addEventListener("complete", function (evt) { handleComplete(evt, comp) });
            var lib = comp.getLibrary();
            loader.loadManifest(lib.properties.manifest);
        };
    var addTick = function () {
        cjs.Ticker.addEventListener('tick', stage);
    },
        dropTick = function () {
            cjs.Ticker.removeEventListener('tick', stage);
        },
        getLabel = function (_this) {
            var currentLabel;
            var currentPosition = _this.currentFrame;
            _this.labels.forEach(function (label, index) {
                if (label.position <= currentPosition) currentLabel = label.label;
            });
            return currentLabel;
        },
        collapse = function (_this, slideName) {
            slide = slideName;
            var label = getLabel(_this);
            switch (label) {
                case 'cbrn start':
                case 'cbrn started':
                    _this.gotoAndPlay(slide + ' expand');
                    break;
                case 'rr stop':
                    _this.gotoAndPlay('rr contract');
                    break;
                case 'br stop':
                    _this.gotoAndPlay('br contract');
                    break;
                case 'cr stop':
                    _this.gotoAndPlay('cr contract');
                    break;
                case 'im stop':
                    _this.gotoAndPlay('im contract');
                    break;
                case 'cbrn stop':
                    _this.gotoAndPlay('cbrn contract');
                    break;
                default:
                    break;
            }
        },
        expand = function (_this, slideName) {
            if (slideName != 'cbrn') {
                _this.gotoAndPlay(slideName + ' expand');
            }
            else {
                _this.gotoAndPlay('cbrn revert');
            }
        },
        revert = function (_this) {
            _this.gotoAndPlay('cbrn revert');
        },
        storeScales = function (listOfObjects) {
            listOfObjects.forEach(function (element) {
                localStorage.setItem(element.name,
                    JSON.stringify({ 'scaleX': element.scaleX, 'scaleY': element.scaleY })
                );
            });
        },
        changeScales = function (listOfObjects, ratio) {
            listOfObjects.forEach(function (element) {
                var sX = JSON.parse(localStorage.getItem(element.name)).scaleX;
                var sY = JSON.parse(localStorage.getItem(element.name)).scaleY;
                // keep width same visual size
                element.scaleX = sX * ratio;
                if (!element.name.includes("TextBox")) {
                    // allow dropdown height to change
                    element.scaleY = sY * ratio;
                }
                if (element.name.includes("reg_button")) {
                    // console.log(element.regX);
                    element.regX = 50 / ratio;
                    element.regY = 25 / ratio;
                }
                if (element.name.includes("log_button")) {
                    // console.log(element.regX);
                    element.regX = 50 / ratio;
                    element.regY = 50 / ratio;
                }
            });
        }, globalListOfObjects,
        setListOfObjects = function (list) {
            globalListOfObjects = list;
        },
        windowResize = function () {
            var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
            //var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
            var ratio = 1024 / w;//w/1024;
            if (w > 980) {
                changeScales(globalListOfObjects, ratio);
            }
        };
    window.onresize = function (event) {
        windowResize();
    };



    (lib.Image = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(0);
    }).prototype = p = new cjs.Sprite();



    (lib.Image_0 = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(1);
    }).prototype = p = new cjs.Sprite();



    (lib.Image_1 = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(2);
    }).prototype = p = new cjs.Sprite();



    (lib.Image_2 = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(3);
    }).prototype = p = new cjs.Sprite();



    (lib.BR_bkg = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(4);
    }).prototype = p = new cjs.Sprite();



    (lib.CR_bkg = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(5);
    }).prototype = p = new cjs.Sprite();



    (lib.IMAAC_bkg = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(6);
    }).prototype = p = new cjs.Sprite();



    (lib.shutterstock_139833604 = function () {
        this.initialize(ss["cbrnSplash_atlas_"]);
        this.gotoAndStop(7);
    }).prototype = p = new cjs.Sprite();
    // helper functions:

    function mc_symbol_clone() {
        var clone = this._cloneProps(new this.constructor(this.mode, this.startPosition, this.loop));
        clone.gotoAndStop(this.currentFrame);
        clone.paused = this.paused;
        clone.framerate = this.framerate;
        return clone;
    }

    function getMCSymbolPrototype(symbol, nominalBounds, frameBounds) {
        var prototype = cjs.extend(symbol, cjs.MovieClip);
        prototype.clone = mc_symbol_clone;
        prototype.nominalBounds = nominalBounds;
        prototype.frameBounds = frameBounds;
        return prototype;
    }


    (lib.Tween14 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#86BE39").s().p("Az/FPIAAqdMAn/AAAIAAKdg");

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-128, -33.5, 256, 67);


    (lib.Tween13 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#86BE39").s().p("Az/FPIAAqdMAn/AAAIAAKdg");

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-128, -33.5, 256, 67);


    (lib.testimaac = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#996600").s().p("AmsKjQixixABj7QgBinA2kYQA/lNBPhPQB8h8A6goQBrhMBvAAQBrAACCBqQAmAfCkCjQBOBPA2EvQArD0AACtQAAD7ixCxQixCyj7AAQj6AAiyiyg");
        this.shape.setTransform(0.05, -24.675);
        this.shape._off = true;

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(3).to({ _off: false }, 0).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-60.5, -109.9, 121.1, 170.5);


    (lib.ClipGroup = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AmVQhIgBAAQkFhYizi2IgEgDQgTgWACgdQACgdAVgTIHzm1QATgRAagBQAZgBAVAQQAtAkA6AUIACABQA6ATA6gBQAZgBATARQAUAQAFAZICCKQQAEAdgSAXQgSAWgcAEQhHAJhGAAQi3AAi7g/gAPFFCQl1h+j+hXQgZgIgNgXQgNgWAFgaQARhngxheIgOgbQgNgVADgZQAEgZATgRIH4m5QAWgRAdAEQAdAEARAXQBDBZAxBlIAAgBQC/GEhwGWIgBAEQgJAcgbAMQgPAIgPAAQgLAAgMgEgAh1DXQhhgfguhbQguhaAghfIABgDQAfhcBWgtQBWgtBdAaIAMAEIAAgBQAvAPAkAgQAlAhAVAsQAtBaghBfQggBghbAtQgrAVgvAEIgQAAQgnAAglgMgAmliVIpzjVIgEgBQgbgLgLgbQgLgbALgaQCfmHGDjAQBmgyBqgeIAFgBQAdgGAYARQAYAQAFAcICBKMQAFAYgMAXQgNAWgXAJIgcAMQgfAQgfAXQg0ApggA7QgMAWgYAKQgMAFgNAAQgLAAgMgEg");
        mask.setTransform(169.0502, 196.157);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#E1E1E1").s().p("AqnNgQAAAAAAgBQABAAAAAAQAAAAgBAAQAAgBAAAAQAAAAgBAAQAAAAAAAAQAAAAgBAAQAAAAAAAAIVQ3EIlwj3IgBAAIAAgCQABAAAAgBQAAAAAAAAQABAAAAAAQAAABAAAAIFxD4IABABIAAABI1RXFIgBABg");
        this.shape.setTransform(316, 203.775);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#E1E1E1").s().p("ABGN2IjNizIgBgBIgBgBIgBgBIgBgBIqBotIgBAAIgBgBIl2lFQgSgQgCgYQgCgYAQgSQAQgSAYgCQAYgBASAPIF2FFIABABIC4CgICli+IgBgDIhSlJQgFgYANgUQANgUAXgFQAYgFAUANQAUANAFAYIA/D6ICKifIABgBIBHhRIHAouIAAgBQAQgSAYgDQAXgCATAPQATAPACAYQADAYgPATInCIvIgCABIgiAoIB4BpIHjpVQAPgTAYgDQAYgDATAQQATAPACAYQADAYgPASInmJYIB5BoIIGp9QAPgTAYgDQAYgCATAPQATAPACAYQADAYgQASInlJYIgCABIgiAoIB5BpIIYqSQAQgTAYgCQAXgDATAPIAAAAQATAPACAYQADAYgQATIn3JsIgCACIn6JHQgQASgYABIgFAAQgVAAgQgOgAC0GtIjcDPICPB9IFmmdIh4hogAjhHbIBDA6IDKiRIhuhggAmxEmIB5BpICfi4Ih5hogAAsCkIBfCHIBxiDIh4hpgAjGAXIB4BpIB7iMIh4hpg");
        this.shape_1.setTransform(148.5563, 114.7994);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFCD55").s().p("AAnCyQhbgIhSgrQg/gfg9gsIiWiBQgTgPgBgYQgCgYAQgTQAQgSAXgBQAYgCATAQICPB9QA1AmBBAeQAvAXA0AJQAbAEAcgDQAegCAcgPIAAAAQBQguA8hFQAYgbATgdQANgTAXgFQAXgEATAMQAVANAFAXQAGAXgNAVQgaAngaAcQhKBUhfA3IgCABQgjAUgpAIQgdAGgfAAIgXgBg");
        this.shape_2.setTransform(78.7456, 174.4593);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFCD55").s().p("ASEJXIyUmIIgQgEIleh9QgpgCgsgKQg4gNgzgUQh/gyhthIQhPgxhjhJQh+hfiLh4IgBAAQgfgbgDgqQgDgqAbggQAcgfApgDQAqgDAgAbQCcCJCpB4QB2BWCIBBQA7AbBDAQQAWAEAVABIAJAAIAJAAQANgBAMgCQAOgDALgFQBvg2BghNQAkgcAihgQAkhqAdghQAFgGAnAaQAnAaAFgGQAZgfAjAEQAeADAjAdQAqAiAGALQAMAVgYAdIhEBUIhdBqQgwA0gzAoQgzApg4AkIBsAmIAGACIRnF4IAHACIA1ASQAoANATAmQASAlgNAoQgNAogmATQgWALgXAAQgQAAgQgGg");
        this.shape_3.setTransform(135.07, 241.7785);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#49932E").s().p("AmogVIDrkOIDPC1IDHjlIDQC1ImxHyg");
        this.shape_4.setTransform(139.525, 146.925);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#67C023").s().p("A2yKEMAchgg1IREMjIpNMjQiWE9j6DuQkCD1k+B2ImhGHgAnogJIGgFpIGxnzIjQi1IjGDlIjQi1g");
        this.shape_5.setTransform(145.9, 145.775);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#0071AB").s().p("Au4SeIn0myIGhmGQJilQFvpGIJMskIObJsMgeXAg8g");
        this.shape_6.setTransform(238.825, 216.75);

        var maskedShapeInstanceList = [this.shape, this.shape_1, this.shape_2, this.shape_3, this.shape_4, this.shape_5, this.shape_6];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup, new cjs.Rectangle(59.4, 84.1, 219.29999999999998, 224.1), null);


    (lib.rr_image = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.shutterstock_139833604();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.2112, 0.2112);

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rr_image, new cjs.Rectangle(0, 0, 255.7, 202.7), null);


    (lib.reg_internal_button = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#000000").s().p("Ag7BNQgQgSAFgYIAAgBIAsAAQgCAQAFAGQAFAGAKAAQAIAAAFgFQAGgEABgJQABgIgEgFQgFgGgOgJQgZgKgLgMQgMgNAEgWQAEgXATgPQATgQAcAAQAdAAAPARQAPARgEAYIgBACIguAAQACgNgDgGQgEgHgIAAQgHAAgFAFQgGAGgBAIQgBAHAEAFQAEAGAPAJQAaAJALANQALAOgDAVQgEAZgUAPQgVAOgcAAQgeAAgPgTg");
        this.shape.setTransform(92.2513, 2.35);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#000000").s().p("Ag7BNQgQgSAFgYIAAgBIAsAAQgCAQAFAGQAFAGAKAAQAIAAAFgFQAGgEABgJQABgIgEgFQgFgGgOgJQgZgKgLgMQgMgNAEgWQAEgXATgPQATgQAcAAQAdAAAPARQAPARgEAYIgBACIguAAQACgNgDgGQgEgHgIAAQgHAAgFAFQgGAGgBAIQgBAHAEAFQAEAGAPAJQAaAJALANQALAOgDAVQgEAZgUAPQgVAOgcAAQgeAAgPgTg");
        this.shape_1.setTransform(77.6013, 2.35);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#000000").s().p("Ag5BJQgRgXAGgjIAEgZQAGgmAWgXQAVgYAgAAQAeAAAOAWQAOAWgGAlIgEAcIhVAAIAAABQgDATAFAMQAGALAPAAQAMAAAJgDQAJgDAMgHIAHAfQgLAJgQAGQgQAGgRAAQghAAgQgXgAgFguQgGAMgEARIAlAAIACgGQACgRgCgIQgDgIgKAAQgKAAgGAKg");
        this.shape_2.setTransform(62.7963, 2.35);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#000000").s().p("Ag7BJQgQgYAGglIAEgXQAGgmAWgXQAWgXAiAAQAbAAAPATQAOAVgFAhIgtAAQACgQgCgKQgDgIgKAAQgMAAgGANQgIAMgDAUIgEAXQgDAWADAMQADAMANgBQAIAAAGgHQAFgIADgNIArAAIABABQgFAegUASQgUASgbAAQghAAgPgXg");
        this.shape_3.setTransform(47.5669, 2.35);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#000000").s().p("Ag7BJQgQgYAGglIAEgXQAGgmAWgXQAWgXAiAAQAbAAAPATQAOAVgFAhIgtAAQACgQgCgKQgDgIgKAAQgMAAgGANQgIAMgDAUIgEAXQgDAWADAMQADAMANgBQAIAAAGgHQAFgIADgNIArAAIABABQgFAegUASQgUASgbAAQghAAgPgXg");
        this.shape_4.setTransform(32.5669, 2.35);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#000000").s().p("AhCBRQgLgQAFgYQAGgdATgNQATgOAhAAIARAAIADgQQACgOgCgGQgDgIgJAAQgHAAgFAHQgEAFgCAMIgugBIgBgBQAFgaAVgQQAVgQAcAAQAaAAAPASQAPAQgGAfIgPBKQgDAOAAALQgBALACANIgwAAIgBgKIgBgLQgIALgKAHQgKAGgMAAQgVAAgLgPgAgPAUQgGAHgCAMQgCAKADAFQADAGAGgBQAGABAGgEQAGgEAEgGIAHgiIgQAAQgIgBgHAJg");
        this.shape_5.setTransform(16.8847, 2.35);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#000000").s().p("AgrBnQgKgPAHgfIAShcIgUAAIAHgjIAUAAIAJguIAwAAIgKAuIAYAAIgHAjIgYAAIgSBcQgCAMABAEQADAFAFAAIAHAAIAHgCIgEAlIgPADIgPABQgVAAgKgOg");
        this.shape_6.setTransform(-2.403, 0.225);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#000000").s().p("Ag7BNQgQgSAFgYIAAgBIAsAAQgCAQAFAGQAFAGAKAAQAIAAAFgFQAGgEABgJQABgIgEgFQgFgGgOgJQgZgKgLgMQgMgNAEgWQAEgXATgPQATgQAcAAQAdAAAPARQAPARgEAYIgBACIguAAQACgNgDgGQgEgHgIAAQgHAAgFAFQgGAGgBAIQgBAHAEAFQAEAGAPAJQAaAJALANQALAOgDAVQgEAZgUAPQgVAOgcAAQgeAAgPgTg");
        this.shape_7.setTransform(-16.3987, 2.35);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#000000").s().p("Ag5BJQgRgXAGgjIAEgZQAGgmAWgXQAVgYAgAAQAeAAAOAWQAOAWgGAlIgEAcIhVAAIAAABQgDATAFAMQAGALAPAAQAMAAAJgDQAJgDAMgHIAHAfQgLAJgQAGQgQAGgRAAQghAAgQgXgAgFguQgGAMgEARIAlAAIACgGQACgRgCgIQgDgIgKAAQgKAAgGAKg");
        this.shape_8.setTransform(-31.2037, 2.35);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#000000").s().p("AhKBLQgKgTAIgmIAWhvIAwAAIgWBvQgFAVADAIQACAJAJAAQAHAAAGgEQAGgDAEgFIAciJIAwAAIglC4IgqAAIABgUQgIAMgLAGQgMAFgNAAQgWAAgKgTg");
        this.shape_9.setTransform(-46.648, 2.525);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#000000").s().p("AgSCCIAQhTQgHAIgIAEQgJADgIAAQgaAAgMgbQgMgbAIgoIAAgDQAKgvASgXQATgYAcAAQAKAAAJAGQAIAGAGAKIAHgSIApAAIgzD/gAgOhLQgIAPgFAZIgBADQgGAbADAOQACAPAMAAQAHAAAGgEQAEgEAFgHIAShZQgCgFgEgCQgFgDgGAAQgLAAgJAPg");
        this.shape_10.setTransform(-63.185, 5.725);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#000000").s().p("Ag5BJQgRgXAGgjIAEgZQAGgmAWgXQAVgYAgAAQAeAAAOAWQAOAWgGAlIgEAcIhVAAIAAABQgDATAFAMQAGALAPAAQAMAAAJgDQAJgDAMgHIAHAfQgLAJgQAGQgQAGgRAAQghAAgQgXgAgFguQgGAMgEARIAlAAIACgGQACgRgCgIQgDgIgKAAQgKAAgGAKg");
        this.shape_11.setTransform(-79.5037, 2.35);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#000000").s().p("Ag+BeIAli4IArAAIgCAXIABAAQAGgMAJgHQAJgHAKAAIAGABIAGABIgOAuIgRgBQgHAAgGAEQgEAEgFAHIgYB9g");
        this.shape_12.setTransform(-92.2, 2.175);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f().s("#FFFFFF").ss(2.9, 1, 1).p("AsslvIZZAAQCYAABsBrQBsBsAACYIAAAAQAACZhsBrQhsBsiYAAI5ZAAQiYAAhshsQhrhrAAiZIAAAAQAAiYBrhsQBshrCYAAg");
        this.shape_13.setTransform(0, 0.025);

        this.shape_14 = new cjs.Shape();
        this.shape_14.graphics.f("#FFFFFF").s().p("AsrFwQiZAAhshsQhshrABiZIAAAAQgBiYBshsQBshrCZAAIZXAAQCZAABrBrQBsBsAACYIAAAAQAACZhsBrQhrBsiZAAg");
        this.shape_14.setTransform(0, 0.025);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_14 }, { t: this.shape_13 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-119.4, -38.2, 238.9, 76.5);


    (lib.red = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#AC2B3A").s().p("Egn/A8DMAAAh4FMBP/AAAMAAAB4Fg");
        this.shape.setTransform(0.025, 0.025);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.red, new cjs.Rectangle(-256, -384.3, 512.1, 768.7), null);


    (lib.orange = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#EF9700").s().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        this.shape.setTransform(0, 0.025);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.orange, new cjs.Rectangle(-384, -384.3, 768, 768.7), null);


    (lib.pin = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#DEDEDE").s().p("AABFlQhGABhCgbQhCgbgyg0Qg0gxgbhCQgbhDABhGQgBhCAYg/QAYg9AtgxIANgNQAygzBBgbQBCgbBGABQBHgBBCAbQBCAbAyAzQAEAGAIAHQAuAxAXA9QAYA/gBBCQABBGgbBDQgbBBgzAyQgxA0hCAbQg/AahFAAIgFAAg");
        this.shape.setTransform(0.0027, 45.55);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#8B3641").s().p("AmpNyQiwixAAj5QgBh8AxhzIAFgOIIlztIIpT7QAyBzgCB8QAAD6iwCwQixCwj5AAIgDABQj3gBiviwgAiHB8QhCAbgyAzIgNANQguAxgXA+QgYA+ABBCQgBBIAbBCQAcBCAzAxQAyA0BCAbQBCAcBGgCQBHABBCgbQBCgbAyg0QAzgyAbhBQAbhCgBhIQABhCgYg+QgYg+gtgxIgNgNQgygyhBgbQhCgbhHAAIgEAAQhFAAg/Aag");
        this.shape_1.setTransform(0.0056, 0.0006);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.pin, new cjs.Rectangle(-60.2, -105.8, 120.4, 211.7), null);


    (lib.login_button2 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AAIB5IAdiTQAGgagFgJQgEgKgNAAQgHAAgHAFQgHAFgGAHIgjCvIg+AAIAwjtIA4AAIgCAfQAMgRAPgJQAPgJARAAQAaAAANAXQAMAYgKAvIgdCTg");
        this.shape.setTransform(45.7599, 0.25);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("AhACrIAvjsIA9AAIgvDsgAgFh7IAIgwIA+AAIgJAwg");
        this.shape_1.setTransform(31.625, -4.8);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("AAIB5IAdiTQAGgagFgJQgEgKgNAAQgHAAgHAFQgHAFgGAHIgjCvIg+AAIAwjtIA4AAIgCAfQAMgRAPgJQAPgJARAAQAaAAANAXQAMAYgKAvIgdCTg");
        this.shape_2.setTransform(4.3099, 0.25);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFFFFF").s().p("AhSCkQgQgFgNgIIAVgwQALAGALAEQAMAEAOAAQARAAAMgOQANgOAEgYIABgGQgJAJgKAFQgLAFgLgBQgiABgPgkQgQgiAKgzIABgFQAMg8AYgeQAYgeAkAAQAOAAALAHQALAIAIANIAIgYIA2AAIguDmQgKAygfAaQgfAbguAAQgOAAgQgFgAgKhjQgLATgHAgIgBAFQgHAiADASQADASAQABQAJgBAGgFQAIgEAGgKIAXhxQgDgHgFgDQgGgEgJAAQgOABgLATg");
        this.shape_3.setTransform(-16.325, 5.05);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FFFFFF").s().p("AhACrIAvjsIA9AAIgvDsgAgFh7IAIgwIA+AAIgJAwg");
        this.shape_4.setTransform(-30.925, -4.8);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FFFFFF").s().p("AhMBjQgUgYAFgeIABgBIA5AAQgDATAGAJQAGAIAOAAQAKAAAHgHQAHgGABgKQACgLgGgHQgGgHgSgLQgggNgPgQQgOgRAEgbQAFgeAZgUQAZgUAkAAQAkAAAUAVQAUAVgGAhIgBABIg7AAQACgQgEgIQgEgJgLAAQgJAAgGAHQgIAHgBAKQgCAKAFAHQAFAGAUALQAhANAPARQAOARgEAcQgGAggZATQgaASgmAAQgmAAgTgYg");
        this.shape_5.setTransform(-47.0401, 0.475);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f().s("#FFFFFF").ss(2.9, 1, 1).p("AsslvIZZAAQCYAABsBrQBsBsAACYIAAAAQAACZhsBrQhsBsiYAAI5ZAAQiYAAhshsQhrhrAAiZIAAAAQAAiYBrhsQBshrCYAAg");
        this.shape_6.setTransform(0, 0.025);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#3770A7").s().p("AsrFwQiZAAhshsQhshrABiZIAAAAQgBiYBshsQBshrCZAAIZXAAQCZAABrBrQBsBsAACYIAAAAQAACZhsBrQhrBsiZAAg");
        this.shape_7.setTransform(0, 0.025);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FFFFFF").s().p("AAJCEIAgigQAGgdgFgKQgEgKgPAAQgIAAgIAFQgHAFgGAIIgnC/IhDAAIA0kDIA+AAIgDAiQANgTAQgKQARgJASAAQAdAAAOAZQANAZgKA1IggCgg");
        this.shape_8.setTransform(50.0797, 0.275);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#FFFFFF").s().p("AhGC7IAzkDIBDAAIgzEDgAgGiGIAKg0IBDAAIgKA0g");
        this.shape_9.setTransform(34.625, -5.225);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#FFFFFF").s().p("AAJCEIAgigQAGgdgFgKQgEgKgPAAQgIAAgIAFQgHAFgGAIIgnC/IhDAAIA0kDIA+AAIgDAiQANgTAQgKQARgJASAAQAdAAAOAZQANAZgKA1IggCgg");
        this.shape_10.setTransform(4.7297, 0.275);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#FFFFFF").s().p("AhaCzQgRgFgOgJIAWg1QAMAIAMAEQANAEAQAAQASAAAOgQQANgPAFgaIABgGQgKAJgLAGQgMAFgMAAQglAAgRgmQgRgnAMg4IABgFQANhBAaghQAaghAnAAQAPAAANAIQANAIAHAOIAJgZIA7AAIgyD6QgLA3giAeQghAdgzAAQgPAAgSgGgAgLhtQgMAVgIAjIgBAFQgHAmADAUQADAUASAAQAJAAAIgFQAHgGAIgKIAYh9QgDgHgGgDQgGgDgJAAQgRAAgLAUg");
        this.shape_11.setTransform(-17.8, 5.525);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#FFFFFF").s().p("AhGC7IAzkDIBDAAIgzEDgAgGiGIAKg0IBDAAIgKA0g");
        this.shape_12.setTransform(-33.775, -5.225);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f("#FFFFFF").s().p("AhUBsQgVgaAGghIAAgBIA+AAQgCAVAGAJQAHAJAPAAQALAAAHgHQAIgHACgLQACgMgHgHQgHgIgUgMQgjgPgQgRQgPgTAEgdQAGghAbgWQAbgWAoAAQAoAAAVAXQAWAYgGAjIgBABIhBAAQACgQgEgKQgFgJgMAAQgJAAgIAIQgIAHgCAKQgCAMAGAHQAGAHAVAMQAlAOAQASQAPATgEAfQgGAjgcAUQgdAVgoAAQgqAAgWgbg");
        this.shape_13.setTransform(-51.3913, 0.525);

        this.shape_14 = new cjs.Shape();
        this.shape_14.graphics.f("#003461").s().p("AsrFwQiZAAhshsQhshrABiZIAAAAQgBiYBshsQBshrCZAAIZXAAQCZAABrBrQBsBsAACYIAAAAQAACZhsBrQhrBsiZAAg");
        this.shape_14.setTransform(0, 0.025);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_7, p: { scaleX: 1, scaleY: 1, x: 0, y: 0.025 } }, { t: this.shape_6, p: { scaleX: 1, scaleY: 1, x: 0, y: 0.025 } }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).to({ state: [{ t: this.shape_7, p: { scaleX: 1.0932, scaleY: 1.0932, x: 0.0495, y: 0.0624 } }, { t: this.shape_6, p: { scaleX: 1.0932, scaleY: 1.0932, x: 0.0495, y: 0.0624 } }, { t: this.shape_13 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_14 }, { t: this.shape_6, p: { scaleX: 1, scaleY: 1, x: 0, y: 0.025 } }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }, 1).to({ state: [{ t: this.shape_14 }, { t: this.shape_6, p: { scaleX: 1, scaleY: 1, x: 0, y: 0.025 } }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }, 1).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-130.4, -41.6, 260.9, 83.30000000000001);


    (lib.im_tint = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#003461").s().p("EhP/A8GMAAAh4LMCf/AAAMAAAB4Lg");
        this.shape.setTransform(512, 384.55);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.im_tint, new cjs.Rectangle(0, 0, 1024, 769.1), null);


    (lib.green_tint = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("rgba(134,190,57,0.498)").s().p("EhP/A8FMAAAh4JMCf/AAAMAAAB4Jg");
        this.shape.setTransform(512, 384.525);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.green_tint, new cjs.Rectangle(0, 0, 1024, 769.1), null);


    (lib.green_menu = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#86BE39").s().p("Az/FPIAAqdMAn/AAAIAAKdg");

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.green_menu, new cjs.Rectangle(-128, -33.5, 256, 67), null);


    (lib.cbrn_shape1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AS1bvMAAAg3dIR5AAQFpAAFKCLQE+CHD2D2QD1D2CHE+QCMFKAAFoIAAAAQAAFpiMFKQiHE+j1D2Qj2D1k+CHQlKCMlpAAgEgkuAbvQloAAlKiMQk/iHj2j1Qj1j2iHk+QiLlKAAlpIAAAAQAAloCMlKQCGk+D2j2QD2j2E+iHQFKiLFoAAMA3jAAAMAAAA3dgAS17ug");
        this.shape.setTransform(474.85, 219.35);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cbrn_shape1, new cjs.Rectangle(62.4, 41.9, 825, 355), null);


    (lib.ClipGroup_16 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("EgvzA94MAAAh7vMBfnAAAMAAAB7vg");
        mask.setTransform(306, 396);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#003262").s().p("AkCE3IgIgCIgFgCIgEgDIAAgFIACgEIAEgCIgCgDIAAgEIABgDIADgCIADgDQgCgCgBgDQgCgCABgEQAAgDACgDQACgDACAAQADgCADAAIAGAAIAHACIANACIgBAFIgGgCIAAABIABADIAAAEQgBAHgFADQgFADgHgCIgGgBIgCADIAAACQAAAAAAAAQAAAAABAAQAAABAAAAQAAAAABAAIADABIAJABQAGABADADQADADgBAFQgBAEgDADQgCACgFABIgFAAIgFAAgAkJEpIgBADQAAABgBAAQAAABABAAQAAABAAAAQABABAAAAQACACAGABQAFABACgCQACAAABgEIAAgCIgCgCIgDgBIgLgCgAj/EGQgCADAAADQgBAFABACQAAABABAAQAAABABAAQAAABABAAQAAAAABAAQAAAAABAAQABAAAAAAQABAAAAAAQABgBAAAAQACgCABgEIgBgIQAAAAAAgBQAAAAgBgBQAAAAgBAAQAAgBgBAAIgBAAQgBAAAAAAQgBAAAAAAQgBABAAAAQAAAAgBABgAg1E0IACgBIAEgHIAPgqIAKgBIAPAvQAAAAAAAAQAAABAAAAQAAAAABAAQAAABAAAAIADABIAAACIgVAAIAAgCIADgBIABgDIgDgKIgQABIgBADIgBAGQAAAEAEAAIAAADIgQAAgAgYEhIgGgVIgIAVIAOAAgAhbE2IAAgFIAAAAIgGAEIgGABQgIgBgCgEQgDgFAAgJQAAgGACgFQACgFAFgCQADgCAGAAIAIACIAAgKIgBgCIgBgCIgCAAIAAgDIAPAAIgBAxIABACIADABIgBACgAhlEVQgCACgBADIgBAHQAAAIACADQABAEADAAIAEgBIADgCIABgEIAAgMQAAgFgBgCQgCgDgDAAQAAAAgBAAQgBAAAAAAQAAABgBAAQAAAAgBABgAiREzIAAgCIACgBIACgCIABgXIAAgBIgBgCIgBgBIgCAAIgDABIgDACQAAAAAAAAQgBABAAAAQAAABAAAAQAAABAAAAIgCAPIAAAFIABACQAAAAAAAAQAAABABAAQAAAAAAAAQABAAAAAAIAAADIgSgCIAAgCIADgBIABgCIABgFIABgUIAAgCIgBgBIgDgBIABgDIAKABIAFAAIgBAFIAAAAIAIgEIAFAAIAFABIADADIABAEIgBAUIAAADIAAACIAAABIACACIABAAIAAADgAAIExQgDgCgBgHIgBgVIgEABIAAgDIACgBIACgDIABgIIAJgBIAAAKIALgBIAAAFIgKAAIABARIABAHIABADQAAAAABAAQAAAAAAAAQABAAAAAAQAAAAABAAIACAAIADgCIADADIgGAEQgCACgEAAIgCAAQgEAAgCgDgAAiEwIADgCIAAgCIgCgUIgBgGIgBgCIgDAAIAAgDIAPgCIAAAGIAAAAIAGgGIAFgCIAFABQAAAAABAAQAAAAABAAQAAAAAAAAQABABAAAAIADAEQADgFADgBQADgCACAAQABgBABAAQABAAAAAAQABAAAAAAQABAAAAABIAEACIACAEIACAGIACAQIAAADIABABIABABIACAAIAAADIgSACIAAgDQABAAAAAAQAAAAAAAAQABAAAAgBQAAAAAAAAIABgCIAAgFIgDgQIgBgDIgCgCIgCAAIgCABIgBABIgCABIgBAEIADAVIABABIADABIAAADIgSACIAAgDIACgBIABgDIgDgWIgBgBIgCgCIgCAAIgDABIgCADIgBADIACAPIABAFIABACIACABIABACIgSACgAi9EvIAAgFIAAAAQgEADgDABIgGABIgFgCIgEgEIAAgFQAAgFAGgDQAHgDAKABIAAgEQABgEgBgBIgCgDIgCgBIgEAAIgCACIgCAEIgHAAIABgHIAOgDIAEAAQADAAAEACQADACABACIAAAIIgCATIABACIAAABIABAAIACABIAAADgAjFEfQgDABgBAEQAAABAAAAQAAABABABQAAAAAAAAQAAABAAAAIAEACIADgBIADgCIABgDIABgHQgHAAgCACgADCEnIADgCIAAgCIgKgpIgBgBIgCgBIgCAAIgBgCIAPgFIABAGIAAAAQADgFACgBQADgCACgBQAHgBAEADQAFAEACAJQACAGgBAFQgBAFgDADQgDADgGACIgIABIACAHIABAEIACABIADAAIAAADIgSAFgADMD1IgDACIgCADIAAAEIADAMQACAFACABQABACAEgBQAEAAAAgFQABgDgCgHIgEgLQgCgCgCAAIgCAAgAB0EnQgFgCgDgDQgDgEgBgHIAAgJQABgFACgBQACgEADgBIAHgCQAKgCAFAEQAFAFACAIQABAGgCAFQgBAFgEADQgGADgEABIgEAAIgFAAgAB4EFQgBAAAAAAQgBAAAAAAQgBAAAAABQgBAAAAABQgBACAAADIAAAIQACAIACAEQADAEADgBQABAAAAAAQABgBAAAAQABAAAAAAQAAgBAAAAIACgDIAAgLQgBgHgDgEQgCgDgDAAIgBAAgAkrEeIAAgDIADAAIABgCIAEgTIAAgFIgBgCIgBAAIgBgBIgDAAIgDACIgCADIgEATIAAADIADABIgBADIgSgEIABgCIACgBIACgCIABgFIAEgTIAAgCIgBgCIgCgBIAAgDIAPADIgBAFIAAAAIAFgCIAEgBQAAAAABAAQAAAAABAAQAAAAABAAQAAAAABAAIAFACIADADIAAAFIAAAGIgEAQIAAACIABADIACABIgBADgACWEXIAFgBQADAEABABQADABADAAQABgBAAAAQABAAAAAAQABgBAAAAQAAAAABgBQAAAAAAAAQABgBAAAAQAAgBAAAAQAAgBgBAAIgBgDIgCgCIgKgCIgEgCIgDgDIgBgDQgBgEACgCIAEgFQAEgDAEAAIAGgBIAHAAIABAIIgFABIgCgDIgDgBIgDAAQAAAAgBAAQAAAAgBAAQAAAAgBABQAAAAAAABQgBAAAAAAQAAAAgBABQAAAAAAABQAAAAABABIABACIACACIAGABIAGACIAEADIACAFQABADgBACIgCAEQgCADgDAAIgGACIgOABgAlbETIAAgDIADAAIABgCIACgFIAEgUIAAgCIgBgBIgCgBIABgDIAPADIgHAbIgBAFIAAABIABABIABABIABABIAAACgAl1EMIABgCIABgBIACAAIABgBIAKgnIABgCIgBgCIAAgBIgCgCIAAgCIAPADIgMAvIAAACIADACIgBADgADtELIACgBIAAgBIABgBIAAgCIgLglIgBgCIgBgCIgBAAIgCAAIgBgDIAKgDIAEgBIAGAUIAFgFQADgDACAAQAEgBADABIAEADIADAHIAFAQIABACIABABIABABIACAAIABADIgSAFIgBgDIACgCIABgCIgGgUIgCgDIgCgBIgCAAQgBAAAAAAQAAAAgBAAQAAABAAAAQAAAAgBABIgCADQAAAAAAABQAAAAAAAAQAAABAAAAQAAABAAAAIAGATIAAABIACABIACAAIABADIgSAFgAmPEFQgJgDgCgGQgDgGADgJQACgGAEgDQADgDAFgBQAEgCAGACIAFADIAEADIACAFIAAAFIgBAIIgXgHQgCAFABAFQABAEAFABIAFABIAGgCIACAEIgJADIgJgBgAmJDlQgDADgCAFIAMAEQACgGgBgDQAAgDgDgBIgCgBQAAAAgBAAQAAABAAAAQgBAAAAAAQgBABAAAAgAm1D4IACgFIAAAAIgHACIgGAAQgHgCgBgGQgCgFADgKQACgFADgEQAEgEAEgBQAFAAAGACIADABIADACIABgDIACgGIAAgDIAAgBIgDgCIABgCIAPAEIgOApIgCAGIABACIACABIgBADgAm0DWQgDABgBADIgEAGQgCAHAAAEQABAEADACIADAAQABAAAAAAQABgBAAAAQABAAAAAAQAAAAABgBIACgDIAEgMQACgEgBgDQgBgCgDgCIgBAAIgDABgAEiD6QgGgDgDgJQgCgGABgFQAAgEAEgEQACgDAGgDIAGgBIAFABIAEACIAEAFIADAHIgXAHQACAGAEADQADACAFgBIAFgDIADgFIAEACIgFAIQgDADgFABIgIACQgDAAgDgCgAElDZQgCABgBAEQgBADABAFIAMgEIgEgHQAAgBgBAAQAAAAgBgBQAAAAAAAAQgBAAAAAAIgCAAgAFHDvIACgCIAAgCIgIgYIgCgBIgBgBIgDAAIgBgDIAOgFIACAFIAAAAIAEgGQACgDADAAIAFgCIAEAKIgHACIgCgCIgDAAIgCABIgBACIgBACIABAFIAHAQIABACIADAAIABACIgRAGgAlODqIADgJIAKACIgCAJgAnpDlQgGgCgCgDQgEgEAAgEQAAgFACgGIAFgIQACgDADgBIAHgBIAIABQAJAEACAGQACAHgDAIQgCAGgEADQgFAEgEAAIgDAAQgDAAgEgCgAngDFQgDABgBADIgEAHQgDAHAAAFQAAAEAEACIADAAIADgCIADgEIADgFQADgIAAgEQgBgFgDgBIgCgBIgCABgAFqDiIACgCIAAgDIgJgYIgCgBIgBAAIgCAAIgBgDIAOgGIALAdIABABIAAABIACABIACAAIABACIgRAHgAoWDSIABgDIADABIABgBIABgBIAPggIAAAAIgbAbIgGgDIADggIABgGIgBAAIgNAdIgBAEIAAACIACACIgBADIgNgGIABgDIACAAIACAAIACgEIAPgiIAAgCIgCgCIABgCIARAHIgDAhIAXgYIAQAIIgBACIgDAAIgBABIgCAEIgPAhIAAACIACADIgBACgAGIDUQgHgDgDgIQgDgHABgEQAAgFADgEQADgEAFgCIAMgEIADAJIgGADIgEgEQgBgBAAAAQgBAAAAAAQgBAAAAAAQAAAAgBABIgCABIgBADIABAFIACAHIAEAHIAEADQADABACgCIAEgCIADgFIAEACIgFAHQgCADgEACQgEACgEAAIgFgBgAG1DBQABgBAAAAQAAAAAAAAQABAAAAgBQAAAAAAAAIABgDIgGgxIAJgFIAiAjIACABIADAAIABACIgTAKIgBgCIACgDIAAgCIgBgDIgGgFIgOAHIABAHIABADQACADAEgBIABACIgPAHgAG7CqIAMgHIgPgQgAFbCyIAKgEIADAIIgJAEgAp3C0IgEgEIAEgHIAEACIgBADIACABIADABIADgCIAHgEIgCgBIAFgiIAAgCIgCgCIACgCIAQAJIgBADIgDgBQAAAAAAAAQgBAAAAAAQAAABAAAAQAAAAAAABQgBAAAAAAQAAABAAABQgBAAAAABQAAAAAAABIgDARIAMgJIADgDIABgCIABgDIgCgCIABgCIANAHIgBACIgCAAIgCAAIgeAXIgIAFQgEACgDAAgAHlCgIAEgDQADADACAAQACABAEgCQAAAAABgBQAAAAABgBQAAAAAAAAQABgBAAAAIgBgEIgCgCIgDgBIgGAAIgEABIgEgBIgDgBIgDgDQgCgEABgCQAAgEADgDIAGgFIAFgCIAGgDIAFAIIgFACIgDgCIgDgBIgDACQAAAAgBAAQAAAAgBAAQAAABAAAAQAAABgBAAQAAABAAAAQAAABAAAAQAAABAAAAQAAAAABABIABACIADABIAMgBIAFACIAEADIABAFIgBAFIgDAEIgFAEQgGAEgHACgAp+CcQgDAAgEgDQgHgDgBgHQgCgHAFgHQAEgGADgCQAFgDAFAAQAFAAAFADIAJAHIgFAIIgGgEQACgDgBgCQAAgBAAAAQgBgBAAAAQAAAAAAgBQgBAAAAAAIgDgBIgDABIgDAEIgFAGIgDAGIAAAGQABACADACIAEABIAFgBIABAFIgHABIgCAAgAIFCNIAEgDQADADADAAQACABADgCIADgDQAAgBAAAAQAAgBAAAAQAAgBAAAAQgBgBAAAAIgCgCIgDgBIgKABIgEAAIgDgCIgDgDIgCgGIADgGQACgCAFgDIAEgDIAHgDIAEAIIgEACIgDgCIgDAAIgDABQgBAAAAAAQgBABAAAAQAAAAgBABQAAAAAAABIAAADIACACIADABIAMgBIAFABIAEAEIABAFIAAAFIgDAEIgFAEIgNAGgAqrCBIABgCIADABIACgCIALgQIABgEIAAgCIAAgBIgCgBIgDgBIgDAAIgDADIgIAMIgCAEIgBADQAAAAAAAAQAAABABAAQAAAAAAAAQAAABABAAIgCACIgPgKIABgCIADAAIACgBIADgEIALgRIAAgCIAAgBIgCgDIACgCIANAIIgEAFIABAAIAFgBIAEABIAEABIAEAEIABAEIgBAEIgCAGIgJAOIgBACIgBABIABACIABABIgCACgAIsB8QgGgCgFgIQgDgEgBgGQgBgEADgFQACgFAEgCIAGgDIAFAAIAEABIAFADIAFAGIgUANQAEAGADABQAEACAEgDQADgCABgCIACgGIAEACQAAAFgDAEQgCADgEADQgGADgFAAIgDAAgAInBbQgCACAAADQAAAEADAFIAKgHQgDgFgDgCIgCgBIgDABgArLBrQgFgBgDgDQgHgEgBgHQgCgGAGgIQAEgGAEgCQAFgCAEAAQAFABAEADIAFAEIACAEIABAFIgBAGIgEAGIgUgNQgEAGAAADQABAFAEACIAFACIAGAAIAAAEIgFABIgEAAgArFBLQgDABgDAFIAKAHIAEgIQAAgEgCgBIgDgBIgDABgAJKBhIAEgDQADACACAAQADABADgCQAAgBABAAQAAgBABAAQAAAAAAgBQAAAAAAgBQAAAAAAgBQAAAAAAgBQAAAAAAgBQAAAAgBgBQAAAAAAgBQAAAAAAAAQgBAAAAAAQAAAAgBAAIgDgBIgFABIgJABIgDgCIgDgCQgCgDAAgDIACgHIAGgGIAFgDIAGgDIAFAHIgEADIgEgCIgDAAIgCACQgBAAAAAAQgBABAAAAQAAABgBAAQAAAAAAABQAAAAAAABQAAAAAAABQAAAAAAABQAAAAABAAIACACIACAAIAGAAIAHgBIAFABQAAAAABAAQAAAAABABQAAAAABABQAAAAAAABQACACAAADIAAAFIgDAEIgEAFQgFADgIADgAr5BfIgJgFIgGgFIgDgFQgBAAAAgBQAAAAAAgBQgBgBAAAAQAAgBAAAAQAAgBAAgBQAAAAABgBQAAAAAAgBQAAAAABAAIADgDIAFAAIAAgDIABgEIADgCIADgBIAFAAQgCgEABgDQAAgCACgEIAFgEIAFgBIAGABIAFAEIADACIACACIALAIIgDAEIgFgEIAAAAIgBADIgCAEQgEAGgFAAQgFABgGgFIgFgFIgEACIAAACIAAACIAKAHQAFAEABAEQACADgEAFQgCAEgEAAIgDABIgFgBgAsCBLIgCABQgCACABADQACAEAEACQAEADACAAQADAAACgCIABgCIAAgDIgMgJgAroAwQgDABgCAEIgDAGQAAABAAAAQAAABAAAAQABABAAAAQAAABABAAQAAABABAAQAAAAABAAQAAAAABAAQABAAAAAAQADAAACgEIAEgHQAAgBAAAAQAAgBgBgBQAAAAAAgBQgBAAAAAAIgDgBIgCAAgAJoBLIAEgDQAEACACAAQACABADgCIADgEQAAgBAAAAQAAgBgBAAQAAgBAAAAQAAAAgBgBIgCgCIgDAAIgFABIgJABIgDgBIgDgDQgCgCAAgEQAAgEACgCIAGgGIAKgHIAGAHIgEADIgEgCIgDAAIgDACQAAAAgBAAQAAABgBAAQAAABAAAAQAAABAAAAIAAAEIACABIADABIAMgDIAFABIAEAEIACAEIAAAFIgDAFIgEAEQgGAFgGACgAsXA0IADgEIAAAAIgIgBQgDgBgCgCIgEgEQgBgCABgDIACgFQADgEAHABQAGABAJAHIACgDIADgFIAAgDIgCgDIgCgBIgEAAQAAAAgBAAQAAAAgBABQAAAAgBABQAAAAgBABIgFgFIAEgFIAOAFIADADIAFAFIABAGQgBADgDAEIgMAOIgBACIAAABIABADIgBACgAsbAkIgBAEQAAAAAAABQABAAAAAAQAAABAAAAQABABAAAAIADACIAEgBIACgCIAFgFQgFgDgEAAIgBAAQgCAAgDACgAKMA5IABgCIAAgDIgNgPIgDgEIgCgBIgBAAIgDABIgBgCIALgKIADAEIACgHIAEgFIAEgCIAEgBIAEACIABgIIAEgEIAEgDIAEAAIAFACIAPAQIABACIACAAIABAAIACAAIACACIgOALIgCgCIABgCIgBgCIgNgQIgDgCIgCgBIgCACIgBABIgBACIAAADIABACIAJAMIADADIACABIABABIADgBIABACIgOAMIgBgCIABgDIgBgCIgJgMIgFgFIgCgBIgBAAIgCAAIgBABIgCACIAAAEIABADIANAPIACABIACAAIACACIgOALgAtEAOIACgCIADABIACgBIAQgSIABgCIAAgBIgBgDIACgCIALAKIgEAEIAAAAQAFgBADABQADABACACIAEADIgHAIIgFgEIABgEQAAgBAAAAQAAAAgBgBQAAAAAAAAQAAgBgBAAIgBgBIgDAAIgCABIgBABIgDACIgLANIAAADIABACIgCACgAK5gBQgEgEgCgFQgBgGACgEQABgDAEgFIAFgDIAFgCIAFABIAFACIAGAFIgSAQQAFAGAEAAQADABAEgDIADgFIABgGIAFABQAAAFgCAEQgBADgEADQgGAGgHAAQgHgBgGgGgAK8gZQgCACAAAEIAEAHIAKgIIgHgGIgCAAIgDABgAtRABQgEgBgDgDQgGgGAAgHQAAgGAGgHQAFgFAEgBQAFgCAFABQAEACAEAEIAEAEIACAFQAAABAAAAQAAABAAABQAAAAAAABQAAAAAAABIgDAFIgEAGIgSgRQgEAFgBAEQAAAFADADIAFADIAGAAIAAAEIgDAAIgHgBgAtFgdIgIAFIAKAIQADgDABgEQACgDgDgCIgEgCIgBABgALkgUIABgCIgBgDIgRgRIgCgBIgCAAIgCABIgCgCIAHgIIAEgDIADAEIACgJIADgEIAEgDIAEAAIAEABIARAQIACABIABABIACAAIABgBIACACIgNANIgCgCIABgCIgBgDIgDgDIgKgKIgEgDIgCAAIgBAAIgCABIgBADIAAAEIABADIAOANIADABIACgBIACACIgNANgAtrgXIgFgDQgEgFAAgDQABgFAEgEIAPgOIgDgEIACgCIADACIACAAIADgCIAEgDIAHAGIgHAHIAIAIIgEADIgHgIIgRARIgBADIABACIACACIABAAIADABIgBAEQgEAAgDgCgAuLg3IACgCIACABIACgBIAEgDIALgKIACgEIAAgDIgBgBIgDgCIgDAAIgDABIgLAKIgDAEIgBACIAAADIgBACIgNgOIACgBIADABIACgBIAEgDIAPgQIAAgCIgBgCIACgCIALALIgFADIABABIAFAAIAEACIADACIADAFIAAAEIgBAEIgSASIgBABIAAACIABACIgCABgAMCg/IgQgOIgDADIgCgCIACgDIgBgDIgBgDIgEgEIAGgGIAHAGIAIgHIADADIgHAIIAMALIAFAEIADABIADgBIABgBIAAgCIABgCIAEAAQAAAEgCADQAAADgDACQgDAEgFAAQgDAAgFgEgAuvhfIACgBIACABIACAAIACgBIACgCIAZgVIADgEIAAgCIgBgCIACgBIANAPIgCABIgCgBIgBAAIgCABIgbAXIgCACIgBABIgBABIAAACIABACIgCABgAMjhjQgFgBgIgGQgGgFgDgHQgDgGACgGQAAgGAFgGIAFgFIAGgEIAIAHIgDAEQgGgCgBAAQgDAAgCADQgEAEACAGQADAHAIAHQAIAGAGACQAGABAEgEIACgEQAAAAAAgBQAAgBAAAAQAAgBAAAAQAAgBAAAAIgDgFIADgEIAJAIIgEAHIgEAFQgFAGgGACIgFABIgGgBgAM5iKQgFgEgCgEQgCgEABgFQABgFADgEIAEgFIAFgCIAEAAIAGACIAHAEIgPATQAGAEADAAQAEAAADgEQACgBABgEIAAgGIAEAAIAAAJQgBAEgDAEQgFAHgHABIgBAAQgFAAgIgGgAM4ihQgCACABADIAGAHIAIgKIgIgEIgBAAQgBAAAAAAQgBAAAAAAQgBAAAAABQgBAAAAABgANhijIABgDIgCgCIgUgOIgCgBIgBAAIgDACIgCgCIAGgJIADgDIAEADIAAAAIAAgFIAAgFIACgDIAEgEIAEgBIAEABIAFACIAOAKIACABIACABIABgBIACgBIACACIgLAPIgCgCIABgDIgCgCIgSgMIgCgBIgBAAIgCABIgBABIgBADIABAEIACADIAQALIACAAIADgBIACABIgLAPgAN3jTIgSgLIgCAEIgCgCIABgDIgBgDIgCgDIgEgDIAFgHIAHAFIAGgJIAEADIgGAJIAUAMIADABQABAAAAAAQAAgBAAAAQABAAAAAAQAAAAAAgBIABgBIABgCIAAgDIAEAAIAAAHIgDAGQgCAEgFABIgBAAQgEAAgEgDgAOBjtQgGgEgCgEQgDgDAAgGQAAgFADgEIAEgFIAEgDIAFgBIAGABIAHAEIgNAUQAHAEADgBQAEAAADgEIABgFIAAgHIAEgBIABAKIgDAIQgEAIgHABIgCAAQgFAAgHgDgAN8kFQgBADACADQABADAFADIAGgLIgIgDQgDAAgCACgAOkkKIABgDIgCgBIgVgNIgCAAIgCAAIgCACIgCgCIAHgNIAFADIAAAAIgBgHQAAgEACgDIACgDIAJAFIgDAGIgEgBIgCACIgBACIABACIABACIABACIASAKIACAAIADgCIACABIgJAQg");
        this.shape.setTransform(171.975, 250.0375);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#003262").s().p("AsyMpQgEgKgCgJQgCgJACgGQABgIAEgFQAEgHAFgEQAHgFAIgDQAIgEAMgCQALgBAGACQAGACAHAFQAIAHADAFQAFAIAEAKIANAeIgEACIgDgEIgDgBIgEABIhEAeQgBAAAAAAQgBAAAAAAQAAABgBAAQAAAAAAABIgCADIABAFIgFABgAr8L1QgIABgMAGQgMAEgIAIQgGAGgCAJQgBAHAEAKIACAFIBLggIgCgHQgEgHgEgDQgCgDgFgCQgFgCgGAAIgEAAgAtdK5IAEgCIAEAFIAEAAIAIgBIAXgHIgKggIgXAHQgGACgBACQgBAAAAAAQgBAAAAAAQAAABgBAAQAAAAAAABIgBAGIgEACIgMgkIAFgCIADAEIADABIADgBIBBgUQAGgCABgBQABAAAAAAQABgBAAAAQAAAAABgBQAAAAAAgBIABgFIAEgCIAMAkIgFACIgDgFIgEAAIgIABIgcAJIAKAgIAhgLIADgCIACgCIABgGIAEgCIAMAlIgFABIgDgDQAAAAAAgBQAAAAgBAAQAAAAgBAAQAAAAgBAAIgDAAIhCAUIgFACIgDACIgCADIAAAEIgFACgAtrJgQgHgEgFgHQgFgIgCgKIgCgNIgBgNIAVgFIACAKIgIAGQgDACgBAEQgBAEABAEQABAEACADQACADADACIAIAAQAEgCACgCQADgCACgEIAKgcQADgFAFgFQAEgDAIgBQAJgCAHADQAHACAFAHQAFAHACAKIADAQIAAAOIgVAGIgCgKQAIgFADgEQAEgGgDgHQAAgEgDgDQgCgEgDgBQgEgBgFABQgFABgCACQgEADgBAEQgCAEgCAHIgEAMQgBAEgEAFQgCAEgEACIgJAEIgHABQgGAAgDgCgAuWGfIgBgdIBEgqIAAgBIg6AEIgDABIgCACIgBAFIgFAAIgBgcIAFAAIACAEIABACIAEABIBXgDIABASIhJAtIAAABIAhgBIAXgBIAIgBQABAAAAgBQABAAAAAAQAAAAABgBQAAAAAAgBIADgFIAEAAIABAcIgEAAIgCgFIgDgCIgDgBIhFACIgHABIgEADIgCAFgAtmEsQgQAAgMgHQgLgGgFgKQgGgKABgPQABgVANgKQAOgLAYACQAMAAALAFQAJADAHAHQAGAGACAJQADAJgBAJQgBAWgNAKQgMAIgUAAIgGAAgAt6DsQgJACgGAEQgFAFgBAGQAAALAKAGQAJAFAVACQAVACAMgFQALgFABgKQAAgHgDgDQgDgFgFgCQgFgDgHgBIgQgDIgMAAIgNABgAs1DOIgBgEIgEgFIhSgvIAEgTIBcgNIAEgCIADgEIAEABIgGAnIgFAAIgBgHQgBgCgDAAIgFgBIgPACIgFAfIAMAFIAFACQAIACABgIIAFABIgFAegAt4CWIAmAUIAEgagAslBoIAAgFIgEgFIgGgGIhGgyIAFgSIBUgDIAIAAIAFgBIADgFIAFACIgLAmIgEgBIgBgGQgBgCgDgBIgFgBIgPAAIgIAeIALAHIAGACQAHADACgIIAFABIgIAegAtiApIAkAYIAHgZgArkhlIABgFIgBgDIgDgCIhDghIgEgBIgDABIgDADIgEgCIAQgiQAFgJAGgIQAFgGAHgFQAGgDAHgBQAFgBAJACQAGABAKAEQAKAFAHAHQAHAGADAHQADAHABAIQAAAJgDAIQgDAKgEAIIgOAdgAsWi4QgGAEgFAIIgCAGIBJAkIADgGQAEgIAAgFQAAgEgBgEQgCgGgHgHQgHgGgLgFQgNgGgJgBIgCAAQgIAAgHAEgAq5jRQgIgBgMgHQgHgFgGgGQgEgGgCgHQgBgGACgHQACgIADgGQAJgOANgDQAMgDAQAJQALAIAEAIQAGAIgBAJQgBAJgGAKQgGAJgHAEQgHAFgIAAIgCAAgArUkHQgCAEABAFQABAEAFAEQADADAKAHQANAIAIABQAJABADgGQACgDAAgDIgDgHIgGgHIgKgHQgNgHgJgCIgDAAQgGAAgDAFgAqDkWIABgDIAAgEIgDgDIg3goIgFgDQgBAAAAgBQAAAAgBAAQAAAAgBAAQAAgBgBAAIgDABIgEACIgEgDIAWgeIAOgQQAFgEAIgEQAGgCAIAAQAHAAAHADQAEABALAHQAKAIAEAGQAGAHACAIQACAHgBAIQAAAHgFAKIgdArgAqmlwQgIADgFAIIgEAFIBCAwIAEgFQAEgGACgHQABgEgBgEQAAgGgGgHQgHgJgJgGQgLgIgJgDIgHAAQgFAAgFABgAn5m9IACgEIABgDIgCgEIgEgEIgugzIgDgCIgDgBIgFACIgDgDIAcgaQAJgIAIgDQAFgEAJgCQAIgBAGABQAHACAGAFQAFACAIAIQAGAGAGAMQAEAIAAAIQAAAJgCAGQgCAFgGAJIgmAlgAoIodQgIACgHAGIgEAEIA2A8IAFgDQAFgFAEgHIACgIQAAgHgEgHQgDgHgJgKQgIgKgKgFQgHgDgGAAIgEAAgAmgoTQgQgEgPgUQgJgNgDgMQgCgNAEgLQAFgLAKgHQASgNAQADQAQAEAPATQAIAMADAJQAEALgCAIQAAAIgGAIQgEAGgJAHQgNAKgMAAIgIgBgAm1pnQgIAGABALQADAMAMAQQAMASALAFQALAGAJgHQAFgDAAgFQACgEgBgHIgFgMIgJgOQgIgKgIgIQgHgGgIgCIgBAAQgGAAgFAEgANXofQgIgCgJgGIgXgUIANgSIAIAFQgDAIAAAGQACAGAGAFQAEADADAAQAFABACgCQADgBAEgEQADgDAAgEQABgFgCgDIgFgMIgFgLIgCgKIAAgJIAFgJQAFgHAHgDQAHgDAJADQAKADAIAFIAJAIIAKAKIgMARIgIgGIACgJQAAgFgBgDIgGgFIgHgEIgHABQgDACgCADIgDAHIABAIIAFALQAFAJABAHQACAIgBAEQgBAGgEAGQgFAHgHADQgFACgGAAIgFAAgAlMpHQABAAAAgBQABAAAAAAQAAgBABAAQAAgBAAAAIABgDIgEgJIgjg7IgDgCIgDgBIgFABIgCgFIA+glIALAUIgIAFIgIgGIgGgBQgDAAgEADIgOAIIASAeIAIgFQADgBABgCIABgFIgBgGIAHgFIAPAaIgHAEIgFgEIgEgBQgDAAgCABIgIAFIATAhIAOgIIADgCIADgCIABgEIAAgDIgCgNIAJgFIAMAYIg9AlgAL9pYIACgEIAGABIADgCIATggIgcgRIgRAeIgBADIAAADIABADIACACIgCAEIghgSIACgEIAFAAIADAAIADgDIADgFIAhg8IABgEIgBgCIgDgEIADgEIAhATIgCADIgGAAIgEACIgQAcIAdAQIAMgVIADgHIAAgEIgDgFIACgEIAhATIgCAEIgFgBIgDABIgDADIglBCQgBABAAAAQAAABAAAAQAAABAAAAQAAABABAAIADAEIgCAFgAKYqMIACgFIAGABIADgCIAPgiIgegNIgNAeIgBAEIAAADQAAAAAAAAQAAABABAAQAAAAAAABQAAAAABAAIACACIgCAFIgigQIABgEIAFABIADgBIAFgJIAcg+IAAgEIgBgDIgDgDIACgFIAjAQIgCAEIgGAAIgDADIgEAGIgKAWIAeANIANgdIgBgDQgBgDgDgCIACgEIAjAPIgCAFIgFgBIgDACIgCACIgfBGIABAEIAEAEIgCAEgAicqiIADgCIACgDIgBgFIgBgFIgYhBIgCgDIgDgBIgEAAIgCgFIBEgYIAIAWIgKADIgHgHIgFgDQgDAAgFACIgPAFIAMAhIAJgDIAFgDQAAAAAAgBQABAAAAgBQAAAAAAgBQAAAAAAAAQABgDgBgEIAIgDIAKAcIgIACIgEgFIgEgCQgBAAgEABIgJADIANAlIATgHIACgCIACgEIABgDIABgMIAJgEIAIAZIhEAZgAhGrAIADgCIACgDIAAgEIgShCIgDgJIgDgCIgFAAIgBgEIAogMQATgEAKADQAKAEAEANQADAJgDAIQgCAJgHAFQgGAGgNADIgLADIAGAXIADAGIACADIACABIAFAAIACAEIgmALgAg4scIgIACIALAnIAGgCQAIgCADgDQADgDABgEQABgGgCgHQgCgGgDgFQgDgDgFgCIgCAAIgIACgAHhrLIgOgCQgOgDgJgIQgIgHgCgMQgCgLAEgRQADgOAIgMQAIgKALgDQAMgFAOADIAMAFIAMAEIgFAVIgJgCQAAgJgDgEQgCgFgHgBQgLgCgIAJQgIAJgFAWQgEATADAMQADALAKACQAFABAEgBQADAAADgEIAGgJIAJACIgEAWIgFAAIgKgBgAADrUIAEgEIADgEIACgIIARhVIATgDIAlBMIAEAHIADADQADABADABIAAAEIgnAHIgBgFQAEAAABgDQACgCgBgDIgBgFIgGgNIgfAFIgBANIAAAGQABAHAIgBIAAAFIgeAFgAAcr8IAagEIgTgngAGDrdQgDgHgEgLIgIgWIgDgDQgCgCgEAAIgEAAIgDAeQAAABAAAAQAAABAAAAQAAABABAAQAAABAAAAIAFADIAAAEIgmgEIAAgFIAFgBIACgBQABgBAAAAQAAgBAAAAQABgBAAAAQAAgBAAgBIABgFIAHhEIAAgDIgCgDIgEgDIAAgEIAqAEIAPAEIAKAEQADADACAEQACACACAFQABAEgBAGQgBAHgDAEQgEAFgEADQgEADgIACIAAABQAFADADAEQAEAEACAHIADAKQACAGADAEQADAFADAAIAAAFgAFosTIAHABIAKAAQAEgCACgDIAEgFIACgIQAAgKgDgGQgEgFgJgCIgJAAgAEWroIggg5QgGgJgDgJIgBAAIAABAIACAEIAGACIAAAFIgcgBIAAgEIAFgBIACgDIABgEIAAgGIABhFIgCgEIgGgCIAAgGIAeABIAmBGIABAAIAAgmIgBgUIgBgEIgCgBIgEgCIAAgFIAcABIAAAEIgFACIgCACIgBADIgCBXg");
        this.shape_1.setTransform(142.475, 135.15);

        var maskedShapeInstanceList = [this.shape, this.shape_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_16, new cjs.Rectangle(50.3, 50.7, 216.09999999999997, 230.5), null);


    (lib.ClipGroup_15 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("EgvzA94MAAAh7vMBfnAAAMAAAB7vg");
        mask.setTransform(306, 396);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#003262").s().p("AlZNCQighFh8h9Qh7h9hDiiQhGipAAi4QAAi3BGipQBDiiB7h9QB8h9CghFQClhHC0AAQC1AACmBHQCfBFB7B9QB7B9BECiQBGCpAAC3QAAC4hGCpQhECih7B9Qh7B9ifBFQimBHi1AAQi0AAilhHg");
        this.shape.setTransform(165.05, 167.85);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f().s("#C0C2C1").ss(5, 0, 0, 4).p("AAAUMQEDAADthlQDkhiCwizQCxizBgjoQBkjwAAkHQAAkGhkjwQhgjnixi0QiwiyjkhiQjthmkDAAQkCAAjtBmQjkBiiwCyQixC0hgDnQhkDwAAEGQAAEHBkDwQBgDoCxCzQCwCzDkBiQDtBlECAAg");
        this.shape_1.setTransform(165.775, 167.7);

        var maskedShapeInstanceList = [this.shape, this.shape_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_15, new cjs.Rectangle(36, 36, 259.6, 263.4), null);


    (lib.ClipGroup_10 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("EgvzA94MAAAh7vMBfnAAAMAAAB7vg");
        mask.setTransform(306, 396);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f().s("#A60010").ss(3, 0, 0, 4).p("AAAPIQDCAACyhMQCrhJCDiGQCFiGBIiuQBLi0AAjFQAAjEhLi0QhIiuiFiFQiDiHirhJQiyhMjCAAQjBAAiyBMQirBJiECHQiECFhICuQhLC0AADEQAADFBLC0QBICuCECGQCECGCrBJQCyBMDBAAg");
        this.shape.setTransform(165.5, 168.6);

        var maskedShapeInstanceList = [this.shape];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_10, new cjs.Rectangle(68.6, 70.3, 193.79999999999998, 196.59999999999997), null);


    (lib.ClipGroup_9 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("ApOK9IAA14QCBggCJAAQC6AACpA6QClA3B+BmQB/BlBGCDQBICIAACUQAACVhICIQhGCDh/BmQh+BlilA3QipA6i6AAQiIAAiCgfg");
        mask.setTransform(59.075, 117.125);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("AleMfQgeg6gzguQgbgYglARQgqASgRgJQgMAPgBAXQgBAVAKAOQgQgCgTgWQgUgXgHgdQgShOBegwQAMgOAZgBQgGgHADgLQAEgJALgKQArggBDgJQA/gIBRAPQA1BEBGAXQBIAYBNgaQAMhmgohNQgKgWhChUQgpg0AHgrQAKg7BchaQDdjZBujIQCEjyA0laQDhDfjdFxQiaEAknDuQgeAXgGAcQgHAcASAdQB3C7g9C0QgaBLg2A6QgyA2g+AZQgdALgqAGQgjAFgLAHQgNAKAGAUQAGARAQAOQggAFgpgbQgXAdAAA5Qgchrgeg6gAmdKgQA8A9BHAkQBLAmBBgCQCbgEASjkQhIAphQgVQhLgVg3hBQgSgGghgCQgigCgiAEQguAFggANQgjANADAPQACAHAJAMQAEAIgUABQgmAAgOARQgrAcgKAdQgKAZANAcQAJgVAOgPQAQAIAQgBQAMgBAmgOQALgCALAAQAaAAAVAQg");
        this.shape.setTransform(62.3545, 96.425);

        var maskedShapeInstanceList = [this.shape];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_9, new cjs.Rectangle(0, 44, 118.2, 146.3), null);


    (lib.ClipGroup_8 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AljKiQilg3h/hlQh+hmhGiDQhIiIAAiVQAAiUBIiIQBGiDB+hlQB/hmClg3QCpg6C6AAQC6AACrA6QCkA3B+BmQB/BlBGCDQBICIAACUQAACVhICIQhGCDh/BmQh+BlikA3QirA6i6AAQi6AAipg6g");
        mask.setTransform(91.6, 73.175);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("AglgEQASAIAVgLQAagOAKABQgGAhgbAGIgIACQgVAAgNgZg");
        this.shape.setTransform(83.675, 109.1529);

        var maskedShapeInstanceList = [this.shape];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_8, new cjs.Rectangle(79.9, 107.1, 7.599999999999994, 4.1000000000000085), null);


    (lib.ClipGroup_7 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("ApOK9IAA14QCBggCJAAQC6AACpA6QClA3B+BmQB/BlBGCDQBICIAACUQAACVhICIQhGCDh/BmQh+BlilA3QipA6i6AAQiIAAiCgfg");
        mask.setTransform(59.075, 73.175);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("Ak4C5QANgXAagLQDDhTB/hQQCYhgB3iCQANEYk3BlQg6ATiiAgQhrAVgIAYQgNgcAOgag");
        this.shape.setTransform(107.5833, 61.375);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#C8D2D9").s().p("AkyEvQAIgeAcgUQDIiYB8iBQCTiZBki3QAsEdjHC/QiICEkuB5QgWgcAIgig");
        this.shape_1.setTransform(100.4244, 39.6);

        var maskedShapeInstanceList = [this.shape, this.shape_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_7, new cjs.Rectangle(69.5, 3.1, 48.7, 82.2), null);


    (lib.ClipGroup_6 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AljKiQilg3h/hlQh+hmhGiDQhIiIAAiVQAAiUBIiIQBGiDB+hlQB/hmClg3QCpg6C6AAQC6AACrA6QCkA3B+BmQB/BlBGCDQBICIAACUQAACVhICIQhGCDh/BmQh+BlikA3QirA6i6AAQi6AAipg6g");
        mask.setTransform(91.6, 73.175);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("AgpgKQACgVAJg3QAHglgHgFQAKgBAHAIQAGAHAAAJQACBHAJAyQAMA8AaA1QhZgeAGhtg");
        this.shape.setTransform(145.7531, 136.6235);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#C8D2D9").s().p("AgwACQgEgUgGg3QgDgkgIgEQAKgDAIAFQAHAFADAJQAWBEAWAuQAbA1AoAtQhdgFgZhsg");
        this.shape_1.setTransform(152.65, 131.6569);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#C8D2D9").s().p("Ag0APQgKgUgUg7QgOgngKgCQAKgGALAEQAJADAGAJQApBFAlArQArA0A4AmQgQADgPAAQhSAAguhfg");
        this.shape_2.setTransform(157.55, 125.2901);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#C8D2D9").s().p("Ag/AfQgUgZgthHQgdgvgNABQALgLAPACQAOACAKAKQBKBOA9AuQBJA3BTAiQgoARglAAQhWAAhHhbg");
        this.shape_3.setTransform(163.075, 115.9882);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#C8D2D9").s().p("Ag5AsQgggUhMg+QgygpgOAFQAIgQARgEQAQgDAPAIQBwA7BVAdQBlAhBpAGQhAA6hLAAQhGAAhOg0g");
        this.shape_4.setTransform(168.35, 106.4862);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#C8D2D9").s().p("AgrAxQgsgLhvgsQhJgdgOALQADgVATgLQARgJAUAEQCXAaBtABQCBACB7ggQhMCBiRAAQgyAAg6gQg");
        this.shape_5.setTransform(172.375, 96.9713);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#C8D2D9").s().p("AkxBRQAQgQAZgEQC1gXB9gmQCUgsCChQQg2DskaAMQg2ACiOgLQhegHgMARQgEgZARgTg");
        this.shape_6.setTransform(174.6128, 78.125);

        var maskedShapeInstanceList = [this.shape, this.shape_1, this.shape_2, this.shape_3, this.shape_4, this.shape_5, this.shape_6];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_6, new cjs.Rectangle(141.6, 65.6, 41.599999999999994, 80.80000000000001), null);


    (lib.ClipGroup_5 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AlaPHIAA+NIK2AAIAAeNg");
        mask.setTransform(106.8, 96.725);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("AldMfQgeg6g0gtQgbgZglARQgqASgQgJQgNAPgBAXQgBAVAKAOQgQgCgTgVQgUgXgHgdQgShPBegwQAMgOAagBQgHgHAEgLQADgKALgIQArghBDgJQBDgIBNAPQA1BEBGAXQBIAYBNgaQAMhmgnhNQgLgWhBhUQgqg0AIgrQAJg7BchbQDdjYBujIQCEjxA0lbQDhDfjdFxQiaEBknDsQgeAZgGAbQgHAbATAeQB2C7g9C0QgaBMg2A5QgyA2g+AZQgdALgqAGQgjAFgKAIQgOAJAHAUQAGARAPAOQgeAGgrgcQgWAcgBA6Qgchrgdg6gAmdKgQA8A9BHAkQBLAmBBgCQCbgEASjkQhHAqhRgWQhLgVg3hBQgSgFgggDQgjgBgiADQguAFggANQgjANADAPQACAHAJAMQAFAJgUgBQgnACgOAQQgqAcgLAeQgKAYANAdQAJgVAPgQQAHAEAJACQAJACAGgBQAMAAAmgOQALgDALAAQAaAAAVAQg");
        this.shape.setTransform(62.3545, 96.4);

        var maskedShapeInstanceList = [this.shape];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_5, new cjs.Rectangle(72.1, 0, 52.60000000000001, 192.8), null);


    (lib.ClipGroup_4 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AmgP1IAA/pINBAAIAAfpg");
        mask.setTransform(98.9, 101.325);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("AglgEQASAIAWgLQAagOAJACQgGAggbAHIgIABQgVAAgNgZg");
        this.shape.setTransform(3.775, 158.0991);

        var maskedShapeInstanceList = [this.shape];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_4, new cjs.Rectangle(0, 0, 0, 0), null);


    (lib.ClipGroup_3_0 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AlaPHIAA+NIK2AAIAAeNg");
        mask.setTransform(37.25, 96.725);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("Ak4C5QANgXAagLQDDhSB/hSQCXhfB4iCQANEYk3BkQg5AUijAgQhrAVgIAYQgNgbAOgbg");
        this.shape.setTransform(38.0333, 105.3);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#C8D2D9").s().p("AkxEvQAHgeAcgVQDIiWB8iCQCTiZBki3QAsEcjHDAQiICFkuB4QgWgcAJgig");
        this.shape_1.setTransform(30.8975, 83.525);

        var maskedShapeInstanceList = [this.shape, this.shape_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_3_0, new cjs.Rectangle(2.5, 47, 67.6, 82.19999999999999), null);


    (lib.ClipGroup_3 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AiAB1IAAjpIEBAAIAADpg");
        mask.setTransform(12.875, 11.7);

        // Layer_3
        this.instance = new lib.Image_2();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.26, 0.26);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_3, new cjs.Rectangle(0, 0, 25.8, 23.4), null);


    (lib.ClipGroup_2_0 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AmgP1IAA/pINBAAIAAfpg");
        mask.setTransform(41.7, 101.325);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#C8D2D9").s().p("AgpgKQABgWAKg2QAHgkgHgGQAKAAAHAHQAFAHABAKQACBHAJAyQAMA7AaA1QhZgeAGhtg");
        this.shape.setTransform(8.7031, 185.5484);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#C8D2D9").s().p("AgwADQgEgUgGg4QgEgkgHgEQAJgDAJAGQAHAFADAJQAVBEAXAtQAbA2AoAsQhegEgYhsg");
        this.shape_1.setTransform(15.575, 180.5721);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#C8D2D9").s().p("AgzAPQgLgUgUg8QgOgmgJgCQAJgHALAEQAJAEAGAJQApBFAkArQAsA0A3AmQgQADgOAAQhRAAguhfg");
        this.shape_2.setTransform(20.475, 174.2009);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#C8D2D9").s().p("Ag/AfQgUgZgthHQgdgvgNAAQALgLAPACQAOACAKALQBKBOA9AuQBJA3BTAhQgoASgmAAQhVAAhHhbg");
        this.shape_3.setTransform(25.975, 164.901);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#C8D2D9").s().p("Ag6AsQgfgUhMg+QgygqgOAFQAIgQASgDQAPgDAPAIQBvA7BWAdQBlAgBpAHQhAA6hLAAQhGAAhPg0g");
        this.shape_4.setTransform(31.25, 155.4053);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#C8D2D9").s().p("AgrAxQgsgLhvgrQhJgdgOALQADgWASgKQASgJAUADQCWAaBtABQCCACB7ggQhMCBiRAAQgyAAg6gQg");
        this.shape_5.setTransform(35.3, 145.8979);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#C8D2D9").s().p("AkxBRQAQgQAZgEQC1gXB9gmQCUgsCChQQg2DskaALQg1ACiPgKQhegHgMARQgEgZARgTg");
        this.shape_6.setTransform(37.5128, 127.075);

        var maskedShapeInstanceList = [this.shape, this.shape_1, this.shape_2, this.shape_3, this.shape_4, this.shape_5, this.shape_6];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_2_0, new cjs.Rectangle(4.5, 114.6, 65, 83.9), null);


    (lib.ClipGroup_2 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("Ah0B1IAAjpIDpAAIAADpg");
        mask.setTransform(11.7, 11.7);

        // Layer_3
        this.instance = new lib.Image_1();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.26, 0.26);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_2, new cjs.Rectangle(0, 0, 23.4, 23.4), null);


    (lib.ClipGroup_1_0 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("EgvzA94MAAAh7vMBfnAAAMAAAB7vg");
        mask.setTransform(306, 396);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f().s("#434344").ss(2, 0, 0, 4).p("AKVCIQggAAgQgeQgRgeAAhPQAAhZASgfQASgfAeAAQA4AAAEBgIBXgGQgMioiIAAQhSAAgpA8QgqA7AABpQAABnAqA7QApA8BRAAQBBAAApgsQAogtAEhGIhYgGQgJBXg0AAgACQDOIBMABIAVhZIB6AAIAVBZIBiAAIh8mwIhgAAIh3GuIh1muIhfAAIiCGwIBXAAIAWhZIB5AAIAVBZIBbgBIABAAIgBgBIAAABAEDArIAqiwIAsCwgAhIArIAqiwIArCwgAp5DPIBOAAIAAlqIBRFqIBFAAIBYlqIAAFqIBaAAIAAmwIiIAAIhFEKIg9kKIiMAAgAsqDPIBfAAIAAmwIhfAAg");
        this.shape.setTransform(165.7, 159.05, 1, 1, 0, 0, 0, 0, -0.9);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("AIaCkQgqg7ABhoQAAhoApg8QApg7BSAAQCIAAAMCnIhXAHQgDhhg5AAQgeAAgRAfQgSAgAABYQAABQAQAeQAQAeAgAAQA0AAAKhXIBYAFQgFBHgoAsQgpAshBAAQhRAAgpg7gAGADZIgVhaIh6AAIgVBaIioAAIgVhaIh5AAIgWBaIhXAAICCmxIBfAAIB2GuIB3muIBfAAIB8GxgAFZA1IgsixIgqCxIBWAAgAANA1IgrixIgqCxIBVAAgAk9DZIAAlqIhYFqIhFAAIhRlqIAAFqIhOAAIAAmxICMAAIA+ELIBEkLICIAAIAAGxgAspDZIAAmxIBeAAIAAGxg");
        this.shape_1.setTransform(165.7, 159.025);

        var maskedShapeInstanceList = [this.shape, this.shape_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_1_0, new cjs.Rectangle(83.7, 135.7, 164.10000000000002, 46.70000000000002), null);


    (lib.ClipGroup_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AhzB0IAAjnIDnAAIAADng");
        mask.setTransform(11.575, 11.575);

        // Layer_3
        this.instance = new lib.Image_0();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.26, 0.26);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_1, new cjs.Rectangle(0, 0, 23.2, 23.2), null);


    (lib.ClipGroup_0 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("Ah2B4IAAjvIDsAAIAADvg");
        mask.setTransform(11.85, 11.975);

        // Layer_3
        this.instance = new lib.Image();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.26, 0.26);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_0, new cjs.Rectangle(0, 0, 23.7, 23.9), null);


    (lib.ClipGroup_11 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        mask_1.graphics.p("AmVQhIgCAAQkFhZizi1QgVgVABgeQABgfAXgUIHym1QATgRAagBQAagBAUAQQAsAkA8AUIACABQA8AUA3gCQAaAAASAQQAUAQAFAZICBKLQAGAegSAYQgSAZgeAEQhHAJhGAAQi2AAi7g/gAPFFCIpzjVQgYgIgNgXQgNgWAEgZQAQhlgwhhQgHgPgHgLQgNgWAEgZQADgZATgRIHzm2QAYgUAeADQAeADATAZQBABVAzBoQC/GEhwGWQgIAdgbAPQgPAIgRAAQgLAAgMgEgAh5DWQhfghgthaQgshaAfhdIABgDQAhhgBagtQBWgpBXAaIANADQBfAhAtBbQAsBaggBfQggBfhbAuQg0AZg1AAQgoAAgpgNgAmliVIpzjVQgdgJgMgcQgMgbALgcQCfmHGDjAQBqgzBmgdQAegIAaAQQAaAQAGAeICBKMQAFAYgNAWQgMAWgYAKIgcAMQghAQgdAXQg1ApgfA7QgMAWgYAKQgNAFgNAAQgLAAgLgEg");
        mask_1.setTransform(169.1203, 196.1775);

        // Layer_3
        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#E0E0E0").s().p("Ag2A3IgDgDQgTgWAAgeQgBgfAXgXQAYgXAeAAQAgAAAXAXQAXAXgBAfQAAAegTAWIgDADQgXAWggAAQgeAAgYgWg");
        this.shape_7.setTransform(117, 117.2);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#9C323E").s().p("Ah4guQgKgXAAgdQAAg3AmgmQAngmA1AAQA2AAAmAmQAnAnAAA2QAAAcgLAYIh4EUgAg2iZQgXAXAAAgQAAAeAUAWIADADQAXAWAfAAQAgAAAXgWIACgDQAUgWAAgeQAAgggWgXQgXgXggAAQgfAAgXAXg");
        this.shape_8.setTransform(117.025, 127.1);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#E0E0E0").s().p("AyyPmIgBgCIACgBIHNC2MAeVgg7Ilwj3QAAAAAAAAQAAAAAAgBQAAAAAAAAQAAgBAAAAIACAAIFxD4IAAABIAAABMgeXAg9IgBAAg");
        this.shape_9.setTransform(263.875, 235.3875);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#E0E0E0").s().p("ABHN2ImeloIAAAAIgCgBIAAgBIgBgBIjNiyIgBgBIgBAAIpboMQgSgPgCgYQgBgYAPgTQAQgSAYgBQAYgCASAQIF1FDIAAAAIABABIACACIC3CfICli+IgBgDIhRlFQgGgYANgUQAMgVAXgFQAXgGAVAMQAVAMAFAYIA/D7ICLifIABgCIBGhRIHAouQAPgTAYgDQAYgCATAPQASAPADAYQACAYgPATInBIvIgCABIgiAoIB5BpIHipVQAPgTAYgDQAYgCATAPQASAPADAYQADAYgQASInBIwIgBABIgjAoIB5BnIIGp9QAQgTAYgCQAXgDATAPQATAPACAYQADAYgPATInmJYIgBABIgjAoIB5BoIIZqSQAPgTAYgCQAYgDASAPQATAQACAYQADAXgPATIn4JtIgBABIn7JHQgPASgYABIgEAAQgWAAgQgOgAC1GrIgBABIgBABIgJAJIjSDGICPB9IFmmcIh5hpgAjhHbIBDA6IDKiRIhuhggAmxEmIB5BpICfi3Ih5hpgAAsClIBfCGIByiDIh5hpgAjGAYIB5BpIB6iNIh4hpg");
        this.shape_10.setTransform(148.6112, 114.7893);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#FACB5F").s().p("AAnCzQhRgGhdguQg7gchBguIiXiCQgSgPgBgYQgCgYAQgSQAPgTAYgBQAYgCASAQICQB8QA+ArA4AaQA5AaArAGQAcAEAagCQAfgEAbgOQBRgvA7hEQAagdARgbQAOgUAXgFQAYgFAUANQAUAOAFAXQAEAYgNAUQgYAlgcAeQhGBRhiA7IgCABQglAUgoAIQgcAGgfAAIgXgBg");
        this.shape_11.setTransform(78.8512, 174.4798);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#FACB5F").s().p("ASEJXIyhmLIgBgBIgBAAIleh9QgogCgtgKQg1gLg3gWQhrgoiChSQhVg1hchFQh7hbiPh7QgfgcgDgqQgDgpAbggQAcggApgDQAqgDAgAbQC1CdCQBkQCOBkBxAzQBIAgA2AKQAbAGAVAAIAEAAIAEAAQARAAAMgDQANgCAMgGIAAAAQBtg0BihOQAkgdAihgQAlhqAdghQAFgGAnAaQAoAaAEgGQAZgeAjADQAeADAjAdQAqAiAGALQAMAVgYAdIhEBUIhdBrQgwAzgzAoQg1Aqg2AjIBzApIADABIRjF2IABABIABAAIA5ATQAoAOATAlQASAmgNAnQgNAogmATQgWALgXAAQgQAAgQgGg");
        this.shape_12.setTransform(135.1285, 241.7785);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f("#65923A").s().p("AmogVIDrkOIDPC1IDHjlIDQC1ImxHyg");
        this.shape_13.setTransform(139.575, 146.925);

        this.shape_14 = new cjs.Shape();
        this.shape_14.graphics.f("#87BF39").s().p("A2zKEMAcigg1IREMkIpMMhQiXE+j7DvQkBD0k+B2ImgGHgAnogKIGgFqIGxnzIjQi1IjGDlIjQi1g");
        this.shape_14.setTransform(145.95, 145.8);

        this.shape_15 = new cjs.Shape();
        this.shape_15.graphics.f("#3771A8").s().p("Au4SeInzmyIGgmHQJjlQFupGIJMsiIOaJrMgeWAg9g");
        this.shape_15.setTransform(238.9, 216.8);

        var maskedShapeInstanceList = [this.shape_7, this.shape_8, this.shape_9, this.shape_10, this.shape_11, this.shape_12, this.shape_13, this.shape_14, this.shape_15];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_15 }, { t: this.shape_14 }, { t: this.shape_13 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_11, new cjs.Rectangle(59.5, 84.2, 219.3, 224), null);


    (lib.white_tab_path = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AWNR+Mg+sAAAMAAAgj6MA+FAAAQAegBAeAAIABAAQDqAADVBbQDOBWCfCgQCgCfBXDPQBaDVAADpQAADqhaDVQhXDPigCfQifCgjOBWQjWBbjqAAIgVAAg");
        this.shape.setTransform(14.775, 144.1);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.white_tab_path, new cjs.Rectangle(-244.4, 29.1, 518.4, 230.00000000000003), null);


    (lib.Path = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("Am/QjQjOhWifigQififhYjPQhajVAAjqQAAjpBajVQBYjPCfifQCfigDOhWQDWhbDpAAQDqAADWBbQDOBWCfCgQCfCfBYDPQBaDVAADpQAADqhaDVQhYDPifCfQifCgjOBWQjWBbjqAAQjpAAjWhbg");
        this.shape.setTransform(159, 144.1);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.Path, new cjs.Rectangle(44, 29.1, 230, 230.00000000000003), null);


    (lib.cr_tint = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#EF9700").s().p("EhP/A8GMAAAh4LMCf/AAAMAAAB4Lg");
        this.shape.setTransform(512, 384.55);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cr_tint, new cjs.Rectangle(0, 0, 1024, 769.1), null);


    (lib.cr_bkg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.CR_bkg();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.3335, 0.3335);

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cr_bkg, new cjs.Rectangle(0, 0, 340.9, 226.8), null);


    (lib.ClipGroup_12 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask_2 = new cjs.Shape();
        mask_2._off = true;
        mask_2.graphics.p("AsXJcQgegBgagOQgpgXgMgtQgNgtAXgpIJEwFQADgGAGgCQAGgDAHACICMAnQBaAUBDgQQCAgeA4gGQAhgEA7ABIAPAAQAIACADAGIJEQDQAPAaAAAeQAAAvghAhQgiAhgvAAg");
        mask_2.setTransform(152.6299, 180.935);

        // Layer_3
        this.shape_16 = new cjs.Shape();
        this.shape_16.graphics.f("#E0E0E0").s().p("AsqWsIgCgBIACgBIIcBDIAquiQAAgBAAAAQAAgBAAAAQAAAAABAAQAAAAABABIQL+TInKihIgBgBQAAAAAAAAQAAgBAAAAQAAAAAAAAQAAAAAAAAIACgCIHMChIABABIAAACIwMeVIgCABIgsOiIgBABg");
        this.shape_16.setTransform(307.625, 231);

        this.shape_17 = new cjs.Shape();
        this.shape_17.graphics.f("#E0E0E0").s().p("AGPPkIoVkKIgHgDIkFiCIgKgEIgJgGIkYiLIgIgDInmjyQgYgMgIgZQgIgZALgYQAMgXAZgIQAZgJAYAMIHmDyIAAAAIDwB2IB7j0IgDgEIiwlDQgMgXAHgZQAHgZAXgNIABgBQAXgMAZAHQAZAIANAXICID5IBnjQIADgEIAyhmIFCrLIAAgCQALgXAZgJQAYgJAYALQAYALAJAYQAJAZgKAYIlDLMIgCACIgZA0ICdBOIAZgyIFDrNIABgDQALgYAagIQAZgJAXAMQAYALAJAZQAIAZgLAYIlFLPIgZAzICbBNIF3szIABABIACgFQALgXAZgIQAZgIAXALQAXAMAIAYQAIAZgLAXIl5M3ICeBOIGEtNIABgDQAMgYAZgJQAZgIAYALQAXAMAJAZQAJAZgMAXIltMfIl5L1IAAABQgLAXgZAIQgKADgKAAQgPAAgNgGgAGGHgIgBABIAAABIgHALIipEMIC6BcIEKoXIichNgAgbKBIBWArICujSIiPhHgAkqH6ICdBOIB2jsIidhPgACrDuICKB1IBVirIidhOgAh8CbICcBPIBci5IichNg");
        this.shape_17.setTransform(123.3415, 123.5366);

        this.shape_18 = new cjs.Shape();
        this.shape_18.graphics.f("#FFCC67").s().p("AhODaQhNgQhLgdIgFgCIi/heQgXgMgJgZQgIgZAMgXQAMgXAZgJQAZgIAXAMIC/BcQBBAaBMAPQA6ALA4gFQAegDAcgKQAhgLAZgZIABgBQAdgfAghtQAdhvAEgNQAIgWAxADQApACAiANQAYAJAMAXQALAYgIAZQgPAtgVApQg1BrhXBWIgCABQghAegpAUQgpAUgtAJQgkAGgoAAQg4AAhDgNg");
        this.shape_18.setTransform(70.7293, 209.0023);

        this.shape_19 = new cjs.Shape();
        this.shape_19.graphics.f("#FFCC67").s().p("AVxHKIhDgEIgCgBI0UhYQgLgBgJgCIl/giQgvAKgwABQg8ACg/gHQiRgUiMguQhbgciDg1QiihCizhYIgCgBQgogVgPgqQgOgrAVgoQAUgqAsgOQAsgPApAVQDFBjDZBSQCzBECGAWQBGAOBKgDQAVgBAVgEQAOgGAQgCIAUgIQAOgGALgJQBkhVBUhuQAegoAKhwQAKh6AWgsQAEgHAxARQAwARADgIQASgnAmgFQAhgFAtAUQA1AZAJAJQATATgSAlQgiBQgPAdIhFCKQglBEgrA3QgrA7gxA1ICFALIAPADIUABXIBHAEQAsAEAeAiQAdAhgCAtQgCAugiAfQggAdgqAAIgGAAg");
        this.shape_19.setTransform(151.8474, 255.4102);

        this.shape_20 = new cjs.Shape();
        this.shape_20.graphics.f("#CE7A00").s().p("AmvB7ICvlfIENCHICVkqIEPCHIlEKIg");
        this.shape_20.setTransform(128.1, 164.8);

        this.shape_21 = new cjs.Shape();
        this.shape_21.graphics.f("#F79900").s().p("A1YP1IB5jzIABAAIC8l7Ihnp8IJlmFIAAgBIIaw6IVjInImUP2QhJF6jHFAQjOFLkyDUIBCJ1gAoIA0IIdENIFDqJIkPiGIAAABIAAAAIiTEpIkOiHg");
        this.shape_21.setTransform(136.925, 171.825);

        this.shape_22 = new cjs.Shape();
        this.shape_22.graphics.f("#2970A6").s().p("AnEYjIqJlEIhCpzIAAgCQERj+DIk8QDHk7BxlkIGRv1IR9GTIwNeVIgsOig");
        this.shape_22.setTransform(271.875, 218.9);

        var maskedShapeInstanceList = [this.shape_16, this.shape_17, this.shape_18, this.shape_19, this.shape_20, this.shape_21, this.shape_22];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_2;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_22 }, { t: this.shape_21 }, { t: this.shape_20 }, { t: this.shape_19 }, { t: this.shape_18 }, { t: this.shape_17 }, { t: this.shape_16 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_12, new cjs.Rectangle(62.1, 120.4, 181.1, 121.1), null);


    (lib.pin_2 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#BCBCBC").s().p("AABFlQhGABhCgbQhCgbgyg0Qg0gxgbhCQgbhDABhGQgBhCAYg/QAYg9AtgxIANgNQAygzBBgbQBCgbBGABQBHgBBCAbQBCAbAyAzQAEAGAIAHQAuAxAXA9QAYA/gBBCQABBGgbBDQgbBBgzAyQgxA0hCAbQg/AahFAAIgFAAg");
        this.shape.setTransform(0.0027, 45.55);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#A2333F").s().p("AmpNyQiwixAAj5QgBh8AxhzIAFgOIIlztIIpT7QAyBzgCB8QAAD6iwCwQixCwj5AAIgDABQj3gBiviwgAiHB8QhCAbgyAzIgNANQguAxgXA+QgYA+ABBCQgBBIAbBCQAcBCAzAxQAyA0BCAbQBCAcBGgCQBHABBCgbQBCgbAyg0QAzgyAbhBQAbhCgBhIQABhCgYg+QgYg+gtgxIgNgNQgygyhBgbQhCgbhHAAIgEAAQhFAAg/Aag");
        this.shape_1.setTransform(0.0056, 0.0006);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.pin_2, new cjs.Rectangle(-60.2, -105.8, 120.4, 211.7), null);


    (lib.ClipGroup_13 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask_3 = new cjs.Shape();
        mask_3._off = true;
        mask_3.graphics.p("AsXJcQgegBgagOQgpgXgMgtQgNgtAXgpIJEwFQADgGAGgCQAGgDAHACICMAnQBaAUBDgQQCAgeA4gGQAhgEA7ABIAPAAQAIACADAGIJEQDQAPAaAAAeQAAAvghAhQgiAhgvAAg");
        mask_3.setTransform(152.6299, 180.935);

        // Layer_3
        this.shape_23 = new cjs.Shape();
        this.shape_23.graphics.f("#E1E1E1").s().p("AsqWsIgCgBIACgBIIcBDIAquiQAAgBAAAAQAAgBAAAAQAAAAABAAQAAAAABABIQL+TInKihIgBgBQAAAAAAAAQAAgBAAAAQAAAAAAAAQAAAAAAAAIACgCIHMChIABABIAAACIwMeVIgCABIgsOiIgBABg");
        this.shape_23.setTransform(307.625, 231);

        this.shape_24 = new cjs.Shape();
        this.shape_24.graphics.f("#E1E1E1").s().p("AGPPkIoVkKIgHgDIkFiCIgKgEIgJgGIkYiLIgIgDInmjyQgYgMgIgZQgIgZALgYQAMgXAZgIQAZgJAYAMIHmDyIAAAAIDwB2IB7j0IgDgEIiwlDQgMgXAHgZQAHgZAXgNIABgBQAXgMAZAHQAZAIANAXICID5IBnjQIADgEIAyhmIFCrLIAAgCQALgXAZgJQAYgJAYALQAYALAJAYQAJAZgKAYIlDLMIgCACIgZA0ICdBOIAZgyIFDrNIABgDQALgYAagIQAZgJAXAMQAYALAJAZQAIAZgLAYIlFLPIgZAzICbBNIF3szIABABIACgFQALgXAZgIQAZgIAXALQAXAMAIAYQAIAZgLAXIl5M3ICeBOIGEtNIABgDQAMgYAZgJQAZgIAYALQAXAMAJAZQAJAZgMAXIltMfIl5L1IAAABQgLAXgZAIQgKADgKAAQgPAAgNgGgAGGHgIgBABIAAABIgHALIipEMIC6BcIEKoXIichNgAgbKBIBWArICujSIiPhHgAkqH6ICdBOIB2jsIidhPgACrDuICKB1IBVirIidhOgAh8CbICcBPIBci5IichNg");
        this.shape_24.setTransform(123.3415, 123.5366);

        this.shape_25 = new cjs.Shape();
        this.shape_25.graphics.f("#FFCD55").s().p("AhODaQhNgQhLgdIgFgCIi/heQgXgMgJgZQgIgZAMgXQAMgXAZgJQAZgIAXAMIC/BcQBBAaBMAPQA6ALA4gFQAegDAcgKQAhgLAZgZIABgBQAdgfAghtQAdhvAEgNQAIgWAxADQApACAiANQAYAJAMAXQALAYgIAZQgPAtgVApQg1BrhXBWIgCABQghAegpAUQgpAUgtAJQgkAGgoAAQg4AAhDgNg");
        this.shape_25.setTransform(70.7293, 209.0023);

        this.shape_26 = new cjs.Shape();
        this.shape_26.graphics.f("#FFCD55").s().p("AVxHKIhDgEIgCgBI0UhYQgLgBgJgCIl/giQgvAKgwABQg8ACg/gHQiRgUiMguQhbgciDg1QiihCizhYIgCgBQgogVgPgqQgOgrAVgoQAUgqAsgOQAsgPApAVQDFBjDZBSQCzBECGAWQBGAOBKgDQAVgBAVgEQAOgGAQgCIAUgIQAOgGALgJQBkhVBUhuQAegoAKhwQAKh6AWgsQAEgHAxARQAwARADgIQASgnAmgFQAhgFAtAUQA1AZAJAJQATATgSAlQgiBQgPAdIhFCKQglBEgrA3QgrA7gxA1ICFALIAPADIUABXIBHAEQAsAEAeAiQAdAhgCAtQgCAugiAfQggAdgqAAIgGAAg");
        this.shape_26.setTransform(151.8474, 255.4102);

        this.shape_27 = new cjs.Shape();
        this.shape_27.graphics.f("#DF7A00").s().p("AmvB7ICvlfIENCHICVkqIEPCHIlEKIg");
        this.shape_27.setTransform(128.1, 164.8);

        this.shape_28 = new cjs.Shape();
        this.shape_28.graphics.f("#FF9900").s().p("A1YP1IB5jzIABAAIC8l7Ihnp8IJlmFIAAgBIIaw6IVjInImUP2QhJF6jHFAQjOFLkyDUIBCJ1gAoIA0IIdENIFDqJIkPiGIAAABIAAAAIiTEpIkOiHg");
        this.shape_28.setTransform(136.925, 171.825);

        this.shape_29 = new cjs.Shape();
        this.shape_29.graphics.f("#0071AB").s().p("AnEYjIqJlEIhCpzIAAgCQERj+DIk8QDHk7BxlkIGRv1IR9GTIwNeVIgsOig");
        this.shape_29.setTransform(271.875, 218.9);

        var maskedShapeInstanceList = [this.shape_23, this.shape_24, this.shape_25, this.shape_26, this.shape_27, this.shape_28, this.shape_29];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_3;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_29 }, { t: this.shape_28 }, { t: this.shape_27 }, { t: this.shape_26 }, { t: this.shape_25 }, { t: this.shape_24 }, { t: this.shape_23 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_13, new cjs.Rectangle(62.1, 120.4, 181.1, 121.1), null);


    (lib.pin_2_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#BDBDBD").s().p("AABFlQhGABhCgbQhCgbgyg0Qg0gxgbhCQgbhDABhGQgBhCAYg/QAYg9AtgxIANgNQAygzBBgbQBCgbBGABQBHgBBCAbQBCAbAyAzQAEAGAIAHQAuAxAXA9QAYA/gBBCQABBGgbBDQgbBBgzAyQgxA0hCAbQg/AahFAAIgFAAg");
        this.shape_2.setTransform(0.0027, 45.55);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#B42E3B").s().p("AmpNyQiwixAAj5QgBh8AxhzIAFgOIIlztIIpT7QAyBzgCB8QAAD6iwCwQixCwj5AAIgDABQj3gBiviwgAiHB8QhCAbgyAzIgNANQguAxgXA+QgYA+ABBCQgBBIAbBCQAcBCAzAxQAyA0BCAbQBCAcBGgCQBHABBCgbQBCgbAyg0QAzgyAbhBQAbhCgBhIQABhCgYg+QgYg+gtgxIgNgNQgygyhBgbQhCgbhHAAIgEAAQhFAAg/Aag");
        this.shape_3.setTransform(0.0056, 0.0006);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_3 }, { t: this.shape_2 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.pin_2_1, new cjs.Rectangle(-60.2, -105.8, 120.4, 211.7), null);


    (lib.svg4 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("ANRWUIgCgCQgXgXABghQABggAXgWIqgq/I2zm7Qg0gPgmgoQg7g+AChUQAChVA+g7IT5zAQAnglA1gOQBSgVBJAsQBJArAVBTIF5XEIKgK/IACgCQAYgVAgABQAgACAWAYQAVAZgCAgQgCAggXAWIm9GpQgXAXggAAIgBAAQgfAAgXgVgAMjLlQgeAdgBApQgBAqAdAeQAdAeApABQAqABAegdQAegdABgpQABgqgdgeQgdgegpgBIgDAAQgoAAgdAcgAGcIuQgiAggBAvQgBAuAgAhQAgAiAvABQAuABAhggQAiggABguQABgvggghQgggigugBIgDAAQgtAAggAfgAGUCzQgXAWgBAgQAAAhAWAXQAWAYAhAAQAgABAYgWQAXgXABggQAAgggWgYQgWgXghgBIgBAAQggAAgXAWgAALyBQgRAFgJAQQgKAQAFASIEUQaQADALAIAJQANANATABQASAAAOgNQAIgJAEgMQADgMgEgLIkVwaQgEgSgQgJQgLgGgMAAIgLABg");
        this.shape.setTransform(142.9054, 144.9087);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.svg4, new cjs.Rectangle(0, 0, 285.9, 289.9), null);


    (lib.polygon114 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#000000").s().p("A8yx4IWh10MAjEBPZg");
        this.shape.setTransform(184.325, 254.05);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.polygon114, new cjs.Rectangle(0, 0, 368.7, 508.1), null);


    (lib.path125 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#000000").s().p("Ay290QgPgsgJgYQjpoUAAp1QgFowDLoKMAqpCD3g");
        this.shape.setTransform(146.4201, 422.025);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.path125, new cjs.Rectangle(0, 0, 292.9, 844.1), null);


    (lib.g1399 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AqaOlQgmizAhi1QAhi0BhiaQBmigCchrIAYgRQhHiwAEi9QADi3BIioQBIipCCiAQCFiGCxhGIAbgLIAIAiQj6BrhjD9QhkD8BsD6QBZDQDHBsQDIBsDfgnIAVBgQhIAegeBGQgeBIAdBIQAIATAIAMIhIBBQjAjAkQAAQkOAAjADBQigChgbDiQgaDhB2DBIgaAYQhsicgni5gACHNNQjGjZgUkmQBqgaBvAMQAPDaCTChQCTChDYAjQACBwglBnQkjgvjGjag");
        this.shape.setTransform(68.6777, 133);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("Au2MyQi2gyiWh2IgVgSIAagXQDaChENgoQEMgoCijaQCGi2gGjiQgGjhiQiuIBGhCQA+AwBNgKQBNgJAvg9QALgNAIgRIBcAdQhDEHCIDqQCJDqEHBFQDaA5DPhZQDPhaBsjFIAiALQhRCsiOB+QiIB6isA+QisA9i3gHQi8gIithRIgagNQh0CWimBbQifBZi3AWQg4AGg4AAQh9AAh5gggACXgeQC0h6BCjQQBCjQhNjMQBgg6BsgTQBpEThZEYQhZEZj0CjQhNhPgthlg");
        this.shape_1.setTransform(164.1, 215.735);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("AQzPSQAekOipjUQiqjUkOgeQjggYi/B1Qi/B2hPDTIhdgfQALhNgvg9Qgug+hNgLQgSgDgRABIgWhfQEGhGCIjsQCGjqhGkGQg7jbi2iIQi2iIjjAGIgHgiQC+gQCzA7QCuA5CMB3QCLB2BUCiQBYCnAQC+IACAdQC9AbChBgQCdBfBuCSQBuCTAuCwQAwC3gbC9IgEAdgAxiBYQC6jkEfg/QEgg/EICBQgeBrhCBZQjDhfjWAuQjVAviJCpQhhg1hJhVg");
        this.shape_2.setTransform(191.236, 98.8388);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.g1399, new cjs.Rectangle(0, 0, 303.5, 300.8), null);


    (lib.g907 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AnqA1QhrliBVlqIOvDfQgaBtAgBqQAgBrBSBMIqYLCQkPj/hqlkg");
        this.shape.setTransform(55.0185, 173.125);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("Aplk4QBqghBMhQQBNhSAZhsIOvDfQhUFpj/EOQj/EOliBrg");
        this.shape_1.setTransform(212, 209.725);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("AizG4QhsgahrAgIkXugQFjhrFpBWQFpBVEOD/IqYLBQhRhMhsgag");
        this.shape_2.setTransform(166.125, 54.1012);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFFFFF").s().p("Ai9DKQhThQgEhyQgDhyBOhTQBPhUBzgDQBygDBTBOQBUBPADBzQADBxhOBUQhPBUhzADIgJAAQhsAAhQhLg");
        this.shape_3.setTransform(138.5981, 138.6);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.g907, new cjs.Rectangle(0, -0.1, 273.5, 271.40000000000003), null);


    (lib.g833 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("ApYGoQADhHAfi7QAUh6gWgTQAjgCAWAZQAUAXABAhQAKDxAkCrQAqDIBaCzQkyhiASl1gAsYGVQAWg5BMiOQAxhdgMgVQAcAHALAbQAKAYgIAbQg5DGgSCTQgVCuAZCqQjdijB0kqgAmPGrQgWhTgijiQgXiUgggNQAngPAjAVQAgATANAkQBmESBoC3QB7DYCqCtQmEAAh3m1gAvCGZQAagmBOhbQAzg8gGgRQAUAKAEAVQADATgKASQhKCGglBpQgrB7gKB/QiGiaCEjFgAB7KwQiahUiDjMQg4hXh3j3QhPiggpgEQAngfAwALQAqALAdAlQDWEUC4CsQDZDKEBCIQhrAihhAAQiEAAhyg+gAxCF/QAegbBXg/QA4gpgBgQQAPAMgCATQgBARgMAOQhcBmg1BSQg/BigjBsQhUihCbiQgAy5E8QAlgSBkglQBCgYADgQQALAQgHASQgFAQgQAKQh0BIhKBBQhXBNg/BeQgliyC8hfgAH8IIQjGgtjRi7QhZhPjRjtQhGhPglgfQgxgqgdAGQAigvA4gCQAzgCAsAhQFEDxECCGQExCdFJBIQi+B7i9AAQhCAAhCgPgAxAC2QirgNiRh2QhlhRhRgWQhtgehcBDQCEimA3hwQA2hxALiDQAGhEhFgnQgrgYgIgGQgZgUgEgcQgmgCgkAYQgiAYgKAeQgPgbALg1QAMg5AlgsQBjh2C1BYQAVABAQALQAOAJAQAUQADgTAUgGQAUgGAaAHQA1AOA4AmQAwAhAtAuQCXCcAlDUQAjDIhIDVQDKhBB0ikQArg9AohYIA/iVQBJiuBUg4QB3hRDmA4QHYB0FzgDQItgEGhj+QhYD7jTCSQjACFkZAkQkEAikxg2QkigykZh5QhAgbhDAfQhBAegZBBQg9Cih3CFQh4CGiVBHQiIBBiGAAQgWAAgXgCgA5rsFQACAPAIARQAHAOAIAJQAPASBCArQA6AyAABGQgPDXAPBsQAVCZBVBIQBiBSB6AGQCNAHAdiAQAkihgrikQgvi1iDiLQg+hBg3gjQg8glgUAVQgFAHgEAMIgGATQAKAIAmASQAeAOAJAQQgyAZgngXQgogXAJgxQgsg5gqgBQhcghg5ARQgwANgeA0QAdgGAdAAIAZABgAIzCIQjOg6jpiLQiuhqjlizQAZhABBgSQA6gQA8AZQG5C7FQBRQGMBfGPgLQkvD0lZAAQiNAAiVgpg");
        this.shape.setTransform(180.5051, 89.4082);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.g833, new cjs.Rectangle(0, -0.1, 361.1, 179.1), null);


    (lib.cbrn_test = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AgJAKIAAgTIATAAIAAATg");
        this.shape.setTransform(133.6, 148.55);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_1.setTransform(126.875, 144.275);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgEAEgEAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALgBAVIAABBg");
        this.shape_2.setTransform(117.7, 144.175);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_3.setTransform(108, 144.275);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_4.setTransform(100.9, 141.925);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_5.setTransform(95.475, 143.05);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAGAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgEAFg");
        this.shape_6.setTransform(87.8, 144.275);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FFFFFF").s().p("AgiA1IAAgNIAthNIgsAAIAAgPIBCAAIAAAMIguBOIAwAAIAAAPg");
        this.shape_7.setTransform(78.975, 144.275);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_8.setTransform(72.45, 141.925);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#FFFFFF").s().p("AASA2IAAhBQgBgOgEgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgVAAIAAhpIATAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_9.setTransform(65.35, 144.175);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#FFFFFF").s().p("AgeAuQgHgIgBgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgTAAIgBgBQAAgMAKgJQAKgKAQAAQAPAAAKAJQAKAKgBARIAAAwIACAMIACALIgUAAIgCgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgIgJgAgMAJQgFAGgBAJQAAAHAEAEQAEAFAGAAQAFAAAGgEQAGgEACgHIAAgVIgOAAQgJAAgEAFg");
        this.shape_10.setTransform(55.8, 144.275);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#FFFFFF").s().p("AgPBJQgIgCgGgDIAFgPIALAEQAGACAGAAQAKAAAFgHQAFgGAAgOIAAgJQgEAFgGAEQgGADgGAAQgRAAgJgNQgJgNAAgTIAAgOQAAgWAJgOQAKgOAQAAQAHAAAGADQAGAEAFAHIABgMIARAAIAABoQAAAVgLALQgKALgTAAQgGAAgIgCgAgNgxQgFAKAAAPIAAAOQAAAMAFAJQAEAIAKAAQAGAAAFgDQAEgEADgFIAAg0QgDgGgEgEQgFgEgGAAQgJAAgFAKg");
        this.shape_11.setTransform(46.125, 146.3);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_12.setTransform(38.775, 144.175);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKAAAOIAAANQAAAOAFAJQAFAKAKAAQALAAAFgKQAGgJAAgOIAAgNQAAgOgGgJQgFgKgLAAQgKAAgFAJg");
        this.shape_13.setTransform(30.4, 144.275);

        this.shape_14 = new cjs.Shape();
        this.shape_14.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_14.setTransform(16.725, 144.275);

        this.shape_15 = new cjs.Shape();
        this.shape_15.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_15.setTransform(7.725, 144.275);

        this.shape_16 = new cjs.Shape();
        this.shape_16.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_16.setTransform(-1.45, 144.175);

        this.shape_17 = new cjs.Shape();
        this.shape_17.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJAAgOIAAgNQAAgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_17.setTransform(-11.15, 144.275);

        this.shape_18 = new cjs.Shape();
        this.shape_18.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgIAAgGgDQgFgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgGAAgFAEg");
        this.shape_18.setTransform(-20.65, 146.2);

        this.shape_19 = new cjs.Shape();
        this.shape_19.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_19.setTransform(-30.075, 144.275);

        this.shape_20 = new cjs.Shape();
        this.shape_20.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_20.setTransform(-38.875, 144.275);

        this.shape_21 = new cjs.Shape();
        this.shape_21.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_21.setTransform(-46.175, 144.175);

        this.shape_22 = new cjs.Shape();
        this.shape_22.graphics.f("#FFFFFF").s().p("AgcBJIgFgBIABgOIADAAIAEABQAFgBAEgFQADgEACgHIAEgLIgihoIAWAAIARBFIACAGIAAAAIAUhLIAVAAIglB5QgEALgFAIQgGAHgMAAIgFgBg");
        this.shape_22.setTransform(-58.4, 146.4);

        this.shape_23 = new cjs.Shape();
        this.shape_23.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_23.setTransform(-67.1988, 144.275);

        this.shape_24 = new cjs.Shape();
        this.shape_24.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_24.setTransform(-76.6, 144.175);

        this.shape_25 = new cjs.Shape();
        this.shape_25.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_25.setTransform(-85.925, 144.275);

        this.shape_26 = new cjs.Shape();
        this.shape_26.graphics.f("#FFFFFF").s().p("AgPBJQgIgCgGgDIAFgPIALAEQAGACAGAAQAKAAAFgHQAFgGAAgOIAAgJQgEAFgGAEQgGADgGAAQgRAAgJgNQgJgNAAgTIAAgOQAAgWAJgOQAKgOAQAAQAHAAAGADQAGAEAFAHIABgMIARAAIAABoQAAAVgLALQgKALgTAAQgGAAgIgCgAgNgxQgFAKAAAPIAAAOQAAAMAFAJQAEAIAKAAQAGAAAFgDQAEgEADgFIAAg0QgDgGgEgEQgFgEgGAAQgJAAgFAKg");
        this.shape_26.setTransform(-95.575, 146.3);

        this.shape_27 = new cjs.Shape();
        this.shape_27.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_27.setTransform(-102.925, 144.175);

        this.shape_28 = new cjs.Shape();
        this.shape_28.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_28.setTransform(-110.925, 144.275);

        this.shape_29 = new cjs.Shape();
        this.shape_29.graphics.f("#FFFFFF").s().p("AArA2IAAg+QAAgQgEgHQgEgGgHAAQgIAAgEAFQgEAFgCAIIAAADIAAAFIAABBIgTAAIAAg+QAAgQgEgHQgEgGgIAAQgGAAgEADQgFADgDAHIAABOIgTAAIAAhpIASAAIABAOQAFgHAGgFQAGgEAKAAQAIAAAHAFQAFAFADAKQAEgKAHgFQAGgFAKAAQANAAAIALQAIALAAAXIAAA+g");
        this.shape_29.setTransform(-123, 144.175);

        this.shape_30 = new cjs.Shape();
        this.shape_30.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_30.setTransform(-134.925, 144.275);

        this.shape_31 = new cjs.Shape();
        this.shape_31.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_31.setTransform(-146.15, 141.925);

        this.shape_32 = new cjs.Shape();
        this.shape_32.graphics.f("#FFFFFF").s().p("AgeAuQgIgIAAgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgUAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgMAJQgFAGgBAJQAAAHAEAEQADAFAHAAQAFAAAGgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_32.setTransform(-153.1, 144.275);

        this.shape_33 = new cjs.Shape();
        this.shape_33.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_33.setTransform(-160, 141.925);

        this.shape_34 = new cjs.Shape();
        this.shape_34.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_34.setTransform(-164.925, 144.175);

        this.shape_35 = new cjs.Shape();
        this.shape_35.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAGAKAJAAQALAAAFgKQAGgJgBgOIAAgNQABgOgGgJQgFgKgLAAQgJAAgGAJg");
        this.shape_35.setTransform(-173.3, 144.275);

        this.shape_36 = new cjs.Shape();
        this.shape_36.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_36.setTransform(-181.325, 143.05);

        this.shape_37 = new cjs.Shape();
        this.shape_37.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_37.setTransform(-186.55, 141.925);

        this.shape_38 = new cjs.Shape();
        this.shape_38.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_38.setTransform(-191.475, 144.175);

        this.shape_39 = new cjs.Shape();
        this.shape_39.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_39.setTransform(-197.675, 144.175);

        this.shape_40 = new cjs.Shape();
        this.shape_40.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_40.setTransform(-205.675, 144.275);

        this.shape_41 = new cjs.Shape();
        this.shape_41.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_41.setTransform(-213.475, 143.05);

        this.shape_42 = new cjs.Shape();
        this.shape_42.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAJgPARAAQAGAAAHADQAFADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgJgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAFAKAKAAQAHAAAEgEQAFgDACgGIAAgzQgCgGgFgEQgEgEgHAAQgKAAgEALg");
        this.shape_42.setTransform(194.05, 113.675);

        this.shape_43 = new cjs.Shape();
        this.shape_43.graphics.f("#FFFFFF").s().p("AASA2IAAhBQgBgOgDgGQgFgGgIAAQgFAAgFADQgFAEgDAHIAABNIgTAAIAAhpIASAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_43.setTransform(184.55, 115.825);

        this.shape_44 = new cjs.Shape();
        this.shape_44.graphics.f("#FFFFFF").s().p("AgdAuQgIgIAAgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAQAAQAPAAAKAJQAKAKAAARIAAAwIAAAMIACALIgUAAIgBgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgHgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAGAAAHgEQAFgEADgHIAAgVIgPAAQgJAAgFAFg");
        this.shape_44.setTransform(175, 115.925);

        this.shape_45 = new cjs.Shape();
        this.shape_45.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_45.setTransform(163.825, 121.475);

        this.shape_46 = new cjs.Shape();
        this.shape_46.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_46.setTransform(159.85, 113.575);

        this.shape_47 = new cjs.Shape();
        this.shape_47.graphics.f("#FFFFFF").s().p("AgeAuQgIgIAAgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgUAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgMAJQgFAGgBAJQAAAHAEAEQADAFAHAAQAFAAAGgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_47.setTransform(152.9, 115.925);

        this.shape_48 = new cjs.Shape();
        this.shape_48.graphics.f("#FFFFFF").s().p("AgJBKQgGgEgEgHIgCAMIgRAAIAAiXIAUAAIAAA6QAEgHAFgDQAGgDAHAAQARAAAJANQAJANAAAWIAAAOQAAAVgJANQgJAMgQAAQgIAAgGgDgAgLgMQgEAEgDAHIAAAxQADAHAEADQAFAEAGAAQAKAAAFgIQAEgJAAgOIAAgOQAAgPgEgJQgGgJgJAAQgGAAgFAEg");
        this.shape_48.setTransform(143.6, 113.675);

        this.shape_49 = new cjs.Shape();
        this.shape_49.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_49.setTransform(136.3, 113.575);

        this.shape_50 = new cjs.Shape();
        this.shape_50.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_50.setTransform(131.375, 115.825);

        this.shape_51 = new cjs.Shape();
        this.shape_51.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_51.setTransform(124.675, 114.7);

        this.shape_52 = new cjs.Shape();
        this.shape_52.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_52.setTransform(115.175, 121.475);

        this.shape_53 = new cjs.Shape();
        this.shape_53.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_53.setTransform(111.2, 113.575);

        this.shape_54 = new cjs.Shape();
        this.shape_54.graphics.f("#FFFFFF").s().p("AgeAuQgHgIAAgOQgBgPALgJQALgIARAAIAPAAIAAgKQgBgKgEgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgSAAIgBgBQAAgMAKgJQALgKAPAAQAPAAAKAJQAKAKAAARIAAAwIABAMIABALIgTAAIgCgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgNAJQgEAGAAAJQgBAHAEAEQAEAFAFAAQAHAAAGgEQAFgEADgHIAAgVIgPAAQgIAAgGAFg");
        this.shape_54.setTransform(104.25, 115.925);

        this.shape_55 = new cjs.Shape();
        this.shape_55.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_55.setTransform(95.1512, 115.925);

        this.shape_56 = new cjs.Shape();
        this.shape_56.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKAAAOIAAANQAAAOAFAJQAFAKAKAAQALAAAFgKQAGgJAAgOIAAgNQAAgOgGgJQgFgKgLAAQgKAAgFAJg");
        this.shape_56.setTransform(85.75, 115.925);

        this.shape_57 = new cjs.Shape();
        this.shape_57.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_57.setTransform(78.65, 113.575);

        this.shape_58 = new cjs.Shape();
        this.shape_58.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_58.setTransform(69.875, 121.475);

        this.shape_59 = new cjs.Shape();
        this.shape_59.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_59.setTransform(63.675, 115.925);

        this.shape_60 = new cjs.Shape();
        this.shape_60.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_60.setTransform(55.875, 114.7);

        this.shape_61 = new cjs.Shape();
        this.shape_61.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAKAKgBARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAGAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgEAFg");
        this.shape_61.setTransform(48.2, 115.925);

        this.shape_62 = new cjs.Shape();
        this.shape_62.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_62.setTransform(40.375, 114.7);

        this.shape_63 = new cjs.Shape();
        this.shape_63.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_63.setTransform(33.025, 115.925);

        this.shape_64 = new cjs.Shape();
        this.shape_64.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_64.setTransform(22.1, 113.575);

        this.shape_65 = new cjs.Shape();
        this.shape_65.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_65.setTransform(17.6, 113.575);

        this.shape_66 = new cjs.Shape();
        this.shape_66.graphics.f("#FFFFFF").s().p("AgeAuQgHgIAAgOQgBgPALgJQALgIARAAIAPAAIAAgKQgBgKgEgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgSAAIgBgBQAAgMAKgJQALgKAPAAQAPAAAKAJQAKAKAAARIAAAwIABAMIABALIgTAAIgCgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgNAJQgEAGAAAJQgBAHAEAEQAEAFAFAAQAGAAAHgEQAFgEADgHIAAgVIgPAAQgIAAgGAFg");
        this.shape_66.setTransform(10.65, 115.925);

        this.shape_67 = new cjs.Shape();
        this.shape_67.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAGAKAJAAQALAAAFgKQAGgJgBgOIAAgNQABgOgGgJQgFgKgLAAQgJAAgGAJg");
        this.shape_67.setTransform(-3.2, 115.925);

        this.shape_68 = new cjs.Shape();
        this.shape_68.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_68.setTransform(-11.225, 114.7);

        this.shape_69 = new cjs.Shape();
        this.shape_69.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_69.setTransform(-23.025, 115.925);

        this.shape_70 = new cjs.Shape();
        this.shape_70.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_70.setTransform(-32.0988, 115.925);

        this.shape_71 = new cjs.Shape();
        this.shape_71.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_71.setTransform(-38.9, 113.575);

        this.shape_72 = new cjs.Shape();
        this.shape_72.graphics.f("#FFFFFF").s().p("AgIA1IgghpIAVAAIASBJIABAIIAAAAIACgIIAShJIAUAAIggBpg");
        this.shape_72.setTransform(-45.45, 115.925);

        this.shape_73 = new cjs.Shape();
        this.shape_73.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_73.setTransform(-52.525, 115.825);

        this.shape_74 = new cjs.Shape();
        this.shape_74.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_74.setTransform(-60.525, 115.925);

        this.shape_75 = new cjs.Shape();
        this.shape_75.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_75.setTransform(-69.525, 115.925);

        this.shape_76 = new cjs.Shape();
        this.shape_76.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_76.setTransform(-82.675, 115.925);

        this.shape_77 = new cjs.Shape();
        this.shape_77.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_77.setTransform(-91.775, 115.925);

        this.shape_78 = new cjs.Shape();
        this.shape_78.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_78.setTransform(-99.075, 115.825);

        this.shape_79 = new cjs.Shape();
        this.shape_79.graphics.f("#FFFFFF").s().p("AgNBNIAAhaIgPAAIAAgOIAPAAIAAgOQgBgRAJgJQAGgJAPAAIAGAAIAHACIgDAOIgDAAIgFgBQgGAAgEAFQgDAGAAAJIAAAOIASAAIAAAOIgSAAIAABag");
        this.shape_79.setTransform(-105.4, 113.475);

        this.shape_80 = new cjs.Shape();
        this.shape_80.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgGAFgIAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEADgHIAAgVIgQAAQgHAAgFAFg");
        this.shape_80.setTransform(-117.75, 115.925);

        this.shape_81 = new cjs.Shape();
        this.shape_81.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_81.setTransform(-131.125, 115.925);

        this.shape_82 = new cjs.Shape();
        this.shape_82.graphics.f("#FFFFFF").s().p("AgdAuQgJgIAAgOQAAgPALgJQAKgIASAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_82.setTransform(-140.15, 115.925);

        this.shape_83 = new cjs.Shape();
        this.shape_83.graphics.f("#FFFFFF").s().p("AgcA/QgKgOAAgXIAAgCQAAgZAKgPQAJgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgJgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgKAAgEALg");
        this.shape_83.setTransform(-154.2, 113.675);

        this.shape_84 = new cjs.Shape();
        this.shape_84.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_84.setTransform(-163.325, 115.925);

        this.shape_85 = new cjs.Shape();
        this.shape_85.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAKgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgKgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgJAAgFALg");
        this.shape_85.setTransform(-173, 113.675);

        this.shape_86 = new cjs.Shape();
        this.shape_86.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_86.setTransform(-179.9, 113.575);

        this.shape_87 = new cjs.Shape();
        this.shape_87.graphics.f("#FFFFFF").s().p("AgIA1IgghpIAVAAIASBJIABAIIAAAAIACgIIAShJIAUAAIgfBpg");
        this.shape_87.setTransform(-186.45, 115.925);

        this.shape_88 = new cjs.Shape();
        this.shape_88.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAFAKAKAAQAKAAAGgKQAGgJgBgOIAAgNQABgOgGgJQgGgKgKAAQgKAAgFAJg");
        this.shape_88.setTransform(-195.7, 115.925);

        this.shape_89 = new cjs.Shape();
        this.shape_89.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_89.setTransform(-203.225, 115.825);

        this.shape_90 = new cjs.Shape();
        this.shape_90.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQARAAAJAOQAJAOAAAWIAAAOQAAATgJANQgJANgRAAQgGAAgGgDQgGgEgFgFIAAAygAgLg2QgFAEgDAGIAAA0QADAGAFAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgGAAgEAEg");
        this.shape_90.setTransform(-211.4, 117.85);

        this.shape_91 = new cjs.Shape();
        this.shape_91.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_91.setTransform(193.675, 87.575);

        this.shape_92 = new cjs.Shape();
        this.shape_92.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_92.setTransform(187.1, 85.225);

        this.shape_93 = new cjs.Shape();
        this.shape_93.graphics.f("#FFFFFF").s().p("AgcA/QgKgOAAgXIAAgCQAAgZAKgPQAIgPARAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgIgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAGAKAJAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgKAAgEALg");
        this.shape_93.setTransform(175.45, 85.325);

        this.shape_94 = new cjs.Shape();
        this.shape_94.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_94.setTransform(165.95, 87.475);

        this.shape_95 = new cjs.Shape();
        this.shape_95.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAOAAIAAgKQABgKgEgFQgFgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_95.setTransform(156.4, 87.575);

        this.shape_96 = new cjs.Shape();
        this.shape_96.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_96.setTransform(145.225, 93.125);

        this.shape_97 = new cjs.Shape();
        this.shape_97.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_97.setTransform(139.125, 87.575);

        this.shape_98 = new cjs.Shape();
        this.shape_98.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_98.setTransform(132.125, 87.475);

        this.shape_99 = new cjs.Shape();
        this.shape_99.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_99.setTransform(124.125, 87.575);

        this.shape_100 = new cjs.Shape();
        this.shape_100.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_100.setTransform(114.65, 87.475);

        this.shape_101 = new cjs.Shape();
        this.shape_101.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgPIAAg/IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA/QAAAIACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_101.setTransform(106.625, 86.35);

        this.shape_102 = new cjs.Shape();
        this.shape_102.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_102.setTransform(100.975, 87.475);

        this.shape_103 = new cjs.Shape();
        this.shape_103.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGABAJQAAAHADAEQADAFAGAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgEAFg");
        this.shape_103.setTransform(92.75, 87.575);

        this.shape_104 = new cjs.Shape();
        this.shape_104.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgQAAQgGAAgGgDQgGgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgHAAgEAEg");
        this.shape_104.setTransform(83.45, 89.5);

        this.shape_105 = new cjs.Shape();
        this.shape_105.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_105.setTransform(71.8, 85.225);

        this.shape_106 = new cjs.Shape();
        this.shape_106.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAJAKABARIAAAwIAAAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGABAJQAAAHADAEQADAFAGAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgEAFg");
        this.shape_106.setTransform(64.85, 87.575);

        this.shape_107 = new cjs.Shape();
        this.shape_107.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_107.setTransform(57.525, 87.475);

        this.shape_108 = new cjs.Shape();
        this.shape_108.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_108.setTransform(49.525, 87.575);

        this.shape_109 = new cjs.Shape();
        this.shape_109.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAKgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgKgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgJAAgFALg");
        this.shape_109.setTransform(39.85, 85.325);

        this.shape_110 = new cjs.Shape();
        this.shape_110.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_110.setTransform(30.725, 87.575);

        this.shape_111 = new cjs.Shape();
        this.shape_111.graphics.f("#FFFFFF").s().p("AgNBNIAAhaIgPAAIAAgOIAPAAIAAgOQgBgRAJgJQAGgJAPAAIAGAAIAHACIgDAOIgDAAIgFgBQgGAAgEAFQgDAGAAAJIAAAOIASAAIAAAOIgSAAIAABag");
        this.shape_111.setTransform(23.3, 85.125);

        this.shape_112 = new cjs.Shape();
        this.shape_112.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_112.setTransform(12.975, 87.475);

        this.shape_113 = new cjs.Shape();
        this.shape_113.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_113.setTransform(4.975, 87.575);

        this.shape_114 = new cjs.Shape();
        this.shape_114.graphics.f("#FFFFFF").s().p("AASBMIAAhBQAAgNgFgGQgDgHgJAAQgFAAgFAEQgEADgDAGIAABOIgVAAIAAiXIAVAAIAAA9QAEgIAGgEQAGgEAHAAQAPAAAIAKQAHALABAVIAABAg");
        this.shape_114.setTransform(-4.5, 85.225);

        this.shape_115 = new cjs.Shape();
        this.shape_115.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgPIAAg/IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA/QAAAIACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_115.setTransform(-12.525, 86.35);

        this.shape_116 = new cjs.Shape();
        this.shape_116.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_116.setTransform(-20.35, 87.575);

        this.shape_117 = new cjs.Shape();
        this.shape_117.graphics.f("#FFFFFF").s().p("AgcA/QgKgOAAgXIAAgCQAAgZAKgPQAIgPARAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgIgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAGAKAJAAQAHAAAEgEQAEgDADgGIAAgzQgDgGgEgEQgEgEgHAAQgKAAgEALg");
        this.shape_117.setTransform(-34.6, 85.325);

        this.shape_118 = new cjs.Shape();
        this.shape_118.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgEgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgVAAIAAhpIATAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_118.setTransform(-44.1, 87.475);

        this.shape_119 = new cjs.Shape();
        this.shape_119.graphics.f("#FFFFFF").s().p("AgeAuQgIgIAAgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgUAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgMAJQgFAGgBAJQAAAHAEAEQADAFAHAAQAFAAAGgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_119.setTransform(-53.65, 87.575);

        this.shape_120 = new cjs.Shape();
        this.shape_120.graphics.f("#FFFFFF").s().p("AAhBHIgKgkIgtAAIgKAkIgUAAIAsiNIASAAIArCNgAgRATIAjAAIgSg9IAAAAg");
        this.shape_120.setTransform(-68.175, 85.75);

        this.shape_121 = new cjs.Shape();
        this.shape_121.graphics.f("#FFFFFF").s().p("AAoBHIAAg3IABg1IgBAAIgiBsIgMAAIgihsIAAAAIABA1IAAA3IgTAAIAAiNIAaAAIAgBzIAAAAIAhhzIAaAAIAACNg");
        this.shape_121.setTransform(-81, 85.75);

        this.shape_122 = new cjs.Shape();
        this.shape_122.graphics.f("#FFFFFF").s().p("AgnBHIAAiNIBPAAIAAAPIg8AAIAAAuIA0AAIAAAOIg0AAIAAAzIA8AAIAAAPg");
        this.shape_122.setTransform(-92.775, 85.75);

        this.shape_123 = new cjs.Shape();
        this.shape_123.graphics.f("#FFFFFF").s().p("AgnBHIAAiNIBQAAIAAAPIg9AAIAAAwIA0AAIAAAOIg0AAIAABAg");
        this.shape_123.setTransform(-102.7, 85.75);

        this.shape_124 = new cjs.Shape();
        this.shape_124.graphics.f("#FFFFFF").s().p("AgcBJIgGgBIADgOIADAAIACABQAGgBADgFQAEgEACgHIADgLIgghoIAVAAIASBFIABAGIAAAAIAUhLIAWAAIglB5QgFALgEAIQgHAHgMAAIgFgBg");
        this.shape_124.setTransform(-116.85, 89.7);

        this.shape_125 = new cjs.Shape();
        this.shape_125.graphics.f("#FFFFFF").s().p("AgJBKQgHgEgDgHIgCAMIgRAAIAAiXIATAAIAAA6QAFgHAFgDQAHgDAGAAQARAAAJANQAJANAAAWIAAAOQAAAVgJANQgJAMgQAAQgIAAgGgDgAgLgMQgFAEgDAHIAAAxQADAHAFADQAFAEAGAAQAKAAAFgIQAEgJAAgOIAAgOQAAgPgEgJQgGgJgJAAQgGAAgFAEg");
        this.shape_125.setTransform(-125.85, 85.325);

        this.shape_126 = new cjs.Shape();
        this.shape_126.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAJgPARAAQAGAAAHADQAFADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgJgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAFAKAKAAQAHAAAEgEQAFgDACgGIAAgzQgCgGgFgEQgEgEgHAAQgKAAgEALg");
        this.shape_126.setTransform(-140.3, 85.325);

        this.shape_127 = new cjs.Shape();
        this.shape_127.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_127.setTransform(-149.425, 87.575);

        this.shape_128 = new cjs.Shape();
        this.shape_128.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_128.setTransform(-156.725, 87.475);

        this.shape_129 = new cjs.Shape();
        this.shape_129.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAFAKAKAAQAKAAAGgKQAGgJgBgOIAAgNQABgOgGgJQgGgKgKAAQgKAAgFAJg");
        this.shape_129.setTransform(-165.1, 87.575);

        this.shape_130 = new cjs.Shape();
        this.shape_130.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_130.setTransform(-174.325, 87.575);

        this.shape_131 = new cjs.Shape();
        this.shape_131.graphics.f("#FFFFFF").s().p("AASA2IAAhBQgBgOgEgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgVAAIAAhpIATAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_131.setTransform(-183.5, 87.475);

        this.shape_132 = new cjs.Shape();
        this.shape_132.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKAAAOIAAANQAAAOAGAJQAGAKAJAAQALAAAFgKQAGgJgBgOIAAgNQABgOgGgJQgFgKgLAAQgJAAgGAJg");
        this.shape_132.setTransform(-193.2, 87.575);

        this.shape_133 = new cjs.Shape();
        this.shape_133.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgHAAgHgDQgFgEgFgFIAAAygAgLg2QgFAEgDAGIAAA0QADAGAFAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgFAAgFAEg");
        this.shape_133.setTransform(-202.7, 89.5);

        this.shape_134 = new cjs.Shape();
        this.shape_134.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_134.setTransform(-212.125, 87.575);

        this.shape_135 = new cjs.Shape();
        this.shape_135.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_135.setTransform(179.125, 59.225);

        this.shape_136 = new cjs.Shape();
        this.shape_136.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_136.setTransform(172.55, 56.875);

        this.shape_137 = new cjs.Shape();
        this.shape_137.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_137.setTransform(163.275, 59.125);

        this.shape_138 = new cjs.Shape();
        this.shape_138.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_138.setTransform(155.275, 59.225);

        this.shape_139 = new cjs.Shape();
        this.shape_139.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAKgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgKgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgJAAgFALg");
        this.shape_139.setTransform(145.6, 56.975);

        this.shape_140 = new cjs.Shape();
        this.shape_140.graphics.f("#FFFFFF").s().p("AASA2IAAhBQAAgOgFgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgUAAIAAhpIASAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAHALABAVIAABBg");
        this.shape_140.setTransform(136.1, 59.125);

        this.shape_141 = new cjs.Shape();
        this.shape_141.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAGAKAJAAQALAAAFgKQAGgJgBgOIAAgNQABgOgGgJQgFgKgLAAQgJAAgGAJg");
        this.shape_141.setTransform(126.4, 59.225);

        this.shape_142 = new cjs.Shape();
        this.shape_142.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQARAAAJAOQAJAOAAAWIAAAOQAAATgJANQgJANgQAAQgIAAgGgDQgFgEgFgFIAAAygAgLg2QgFAEgDAGIAAA0QADAGAFAEQAEACAHAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgFAAgFAEg");
        this.shape_142.setTransform(116.9, 61.15);

        this.shape_143 = new cjs.Shape();
        this.shape_143.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_143.setTransform(107.475, 59.225);

        this.shape_144 = new cjs.Shape();
        this.shape_144.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_144.setTransform(98.675, 59.225);

        this.shape_145 = new cjs.Shape();
        this.shape_145.graphics.f("#FFFFFF").s().p("AAaBHQgEgDgBgHIAAgNIAAgNQAAgLgGgIQgFgGgKgBIgaAAIAAA+IgTAAIAAiNIAsAAQATAAALALQALAKAAAUQAAALgFAIQgFAIgKAEQALAEAFAIQAEAIAAANIAAANQAAAHACAFQABAGADADIAAACgAgagEIAYAAQALAAAGgHQAFgGAAgMQAAgNgFgGQgGgHgKAAIgZAAg");
        this.shape_145.setTransform(88.725, 57.4);

        this.shape_146 = new cjs.Shape();
        this.shape_146.graphics.f("#FFFFFF").s().p("AAbBHIg1hpIAAAAIAABpIgUAAIAAiNIAUAAIA1BoIAAAAIAAhoIAUAAIAACNg");
        this.shape_146.setTransform(76.6, 57.4);

        this.shape_147 = new cjs.Shape();
        this.shape_147.graphics.f("#FFFFFF").s().p("AAaBHQgEgDgBgHIAAgNIAAgNQAAgLgGgIQgFgGgKgBIgaAAIAAA+IgTAAIAAiNIAsAAQATAAALALQALAKAAAUQAAALgFAIQgFAIgKAEQALAEAFAIQAEAIAAANIAAANQAAAHACAFQABAGADADIAAACgAgagEIAYAAQALAAAGgHQAFgGAAgMQAAgNgFgGQgGgHgKAAIgZAAg");
        this.shape_147.setTransform(65.175, 57.4);

        this.shape_148 = new cjs.Shape();
        this.shape_148.graphics.f("#FFFFFF").s().p("AgqBHIAAiNIAnAAQATAAAMAJQALAKAAATQAAALgFAHQgFAIgJAEQALACAGAIQAGAKAAANQAAAUgLAKQgLAKgTAAgAgXA4IAZAAQAKAAAGgGQAGgHAAgMQAAgMgFgHQgFgHgKAAIgbAAgAgXgJIAWAAQAJAAAFgFQAGgHAAgKQAAgNgGgFQgGgGgKAAIgUAAg");
        this.shape_148.setTransform(53.925, 57.4);

        this.shape_149 = new cjs.Shape();
        this.shape_149.graphics.f("#FFFFFF").s().p("AgfA7QgNgOAAgaIAAgmQAAgZANgOQAMgOAUAAQAVAAALALQALALAAAWIAAABIgTAAQABgPgHgIQgFgHgNAAQgLAAgHAKQgHAKABASIAAAmQgBATAHAKQAHAKALAAQANAAAFgHQAHgHgBgQIATAAIAAAAQABAVgMAMQgLAMgVAAQgTAAgNgOg");
        this.shape_149.setTransform(43, 57.4);

        this.shape_150 = new cjs.Shape();
        this.shape_150.graphics.f("#FFFFFF").s().p("AgJAKIAAgTIATAAIAAATg");
        this.shape_150.setTransform(30.8, 63.5);

        this.shape_151 = new cjs.Shape();
        this.shape_151.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_151.setTransform(24.075, 59.225);

        this.shape_152 = new cjs.Shape();
        this.shape_152.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_152.setTransform(15.275, 59.225);

        this.shape_153 = new cjs.Shape();
        this.shape_153.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_153.setTransform(6.2012, 59.225);

        this.shape_154 = new cjs.Shape();
        this.shape_154.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_154.setTransform(-1.025, 59.125);

        this.shape_155 = new cjs.Shape();
        this.shape_155.graphics.f("#FFFFFF").s().p("AgcArQgJgLABgYIAAg9IATAAIAAA+QAAAQAEAHQAEAGAIAAQAGAAAFgDQAFgDACgHIAAhOIAUAAIAABpIgRAAIgCgOQgDAHgHAFQgHAEgHAAQgPAAgHgLg");
        this.shape_155.setTransform(-9.4, 59.325);

        this.shape_156 = new cjs.Shape();
        this.shape_156.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKAAAOIAAANQAAAOAFAJQAFAKAKAAQALAAAFgKQAGgJAAgOIAAgNQAAgOgGgJQgFgKgLAAQgKAAgFAJg");
        this.shape_156.setTransform(-19.1, 59.225);

        this.shape_157 = new cjs.Shape();
        this.shape_157.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_157.setTransform(-28.325, 59.225);

        this.shape_158 = new cjs.Shape();
        this.shape_158.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_158.setTransform(-37.125, 59.225);

        this.shape_159 = new cjs.Shape();
        this.shape_159.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_159.setTransform(-44.425, 59.125);

        this.shape_160 = new cjs.Shape();
        this.shape_160.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAKgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgKgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgJAAgFALg");
        this.shape_160.setTransform(-57.35, 56.975);

        this.shape_161 = new cjs.Shape();
        this.shape_161.graphics.f("#FFFFFF").s().p("AASA2IAAhBQAAgOgFgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgUAAIAAhpIASAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAHALABAVIAABBg");
        this.shape_161.setTransform(-66.85, 59.125);

        this.shape_162 = new cjs.Shape();
        this.shape_162.graphics.f("#FFFFFF").s().p("AgeAuQgHgIgBgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgTAAIgBgBQAAgMAKgJQAKgKAQAAQAPAAAKAJQAKAKAAARIAAAwIABAMIACALIgUAAIgCgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgIgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAHAAAFgEQAGgEACgHIAAgVIgOAAQgJAAgFAFg");
        this.shape_162.setTransform(-76.4, 59.225);

        this.shape_163 = new cjs.Shape();
        this.shape_163.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_163.setTransform(-89.775, 59.225);

        this.shape_164 = new cjs.Shape();
        this.shape_164.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_164.setTransform(-96.35, 56.875);

        this.shape_165 = new cjs.Shape();
        this.shape_165.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAFAKAKAAQAKAAAGgKQAGgJgBgOIAAgNQABgOgGgJQgGgKgKAAQgKAAgFAJg");
        this.shape_165.setTransform(-103.45, 59.225);

        this.shape_166 = new cjs.Shape();
        this.shape_166.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKAAAOIAAANQAAAOAFAJQAFAKAKAAQALAAAFgKQAGgJAAgOIAAgNQAAgOgGgJQgFgKgLAAQgKAAgFAJg");
        this.shape_166.setTransform(-113.15, 59.225);

        this.shape_167 = new cjs.Shape();
        this.shape_167.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_167.setTransform(-121.175, 58);

        this.shape_168 = new cjs.Shape();
        this.shape_168.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_168.setTransform(-130.75, 56.875);

        this.shape_169 = new cjs.Shape();
        this.shape_169.graphics.f("#FFFFFF").s().p("AgeAuQgHgIgBgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgTAAIgBgBQAAgMAKgJQAKgKAQAAQAPAAAKAJQAKAKgBARIAAAwIACAMIACALIgUAAIgCgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgIgJgAgNAJQgEAGgBAJQAAAHAEAEQAEAFAGAAQAFAAAGgEQAGgEACgHIAAgVIgOAAQgJAAgFAFg");
        this.shape_169.setTransform(-137.7, 59.225);

        this.shape_170 = new cjs.Shape();
        this.shape_170.graphics.f("#FFFFFF").s().p("AASA2IAAhBQAAgOgFgGQgEgGgIAAQgFAAgFADQgFAEgDAHIAABNIgTAAIAAhpIASAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_170.setTransform(-147.2, 59.125);

        this.shape_171 = new cjs.Shape();
        this.shape_171.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAFAKAKAAQAKAAAGgKQAGgJAAgOIAAgNQAAgOgGgJQgGgKgKAAQgKAAgFAJg");
        this.shape_171.setTransform(-156.9, 59.225);

        this.shape_172 = new cjs.Shape();
        this.shape_172.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_172.setTransform(-164, 56.875);

        this.shape_173 = new cjs.Shape();
        this.shape_173.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_173.setTransform(-169.425, 58);

        this.shape_174 = new cjs.Shape();
        this.shape_174.graphics.f("#FFFFFF").s().p("AgeAuQgHgIgBgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgEgFQgEgGgHAAQgHAAgEAFQgEAFAAAHIgTAAIgBgBQAAgMAKgJQALgKAPAAQAPAAAKAJQAKAKAAARIAAAwIABAMIABALIgTAAIgCgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgNAJQgEAGAAAJQgBAHAEAEQAEAFAFAAQAHAAAGgEQAFgEACgHIAAgVIgOAAQgIAAgGAFg");
        this.shape_174.setTransform(-177.1, 59.225);

        this.shape_175 = new cjs.Shape();
        this.shape_175.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_175.setTransform(-184.425, 59.125);

        this.shape_176 = new cjs.Shape();
        this.shape_176.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_176.setTransform(-192.425, 59.225);

        this.shape_177 = new cjs.Shape();
        this.shape_177.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQARAAAJAOQAJAOAAAWIAAAOQAAATgJANQgJANgQAAQgIAAgGgDQgFgEgFgFIAAAygAgLg2QgFAEgDAGIAAA0QADAGAFAEQAEACAHAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgFAAgFAEg");
        this.shape_177.setTransform(-201.7, 61.15);

        this.shape_178 = new cjs.Shape();
        this.shape_178.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_178.setTransform(-211.6, 59.225);

        this.shape_179 = new cjs.Shape();
        this.shape_179.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAJgPARAAQAGAAAHADQAFADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgRAAgJgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAFAKAKAAQAHAAAEgEQAFgDACgGIAAgzQgCgGgFgEQgEgEgHAAQgKAAgEALg");
        this.shape_179.setTransform(209.35, 28.625);

        this.shape_180 = new cjs.Shape();
        this.shape_180.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgFAEgDAHIAABNIgTAAIAAhpIASAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_180.setTransform(199.85, 30.775);

        this.shape_181 = new cjs.Shape();
        this.shape_181.graphics.f("#FFFFFF").s().p("AgdAuQgIgIAAgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAKAKAAARIAAAwIAAAMIACALIgUAAIgBgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgHgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgFAFg");
        this.shape_181.setTransform(190.3, 30.875);

        this.shape_182 = new cjs.Shape();
        this.shape_182.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_182.setTransform(179.125, 36.425);

        this.shape_183 = new cjs.Shape();
        this.shape_183.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_183.setTransform(173.025, 30.875);

        this.shape_184 = new cjs.Shape();
        this.shape_184.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_184.setTransform(164.325, 30.875);

        this.shape_185 = new cjs.Shape();
        this.shape_185.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_185.setTransform(155.525, 30.875);

        this.shape_186 = new cjs.Shape();
        this.shape_186.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgEAEgEAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALgBAVIAABBg");
        this.shape_186.setTransform(146.05, 30.775);

        this.shape_187 = new cjs.Shape();
        this.shape_187.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAKgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgKgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgJAAgFALg");
        this.shape_187.setTransform(136.15, 28.625);

        this.shape_188 = new cjs.Shape();
        this.shape_188.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_188.setTransform(127.025, 30.875);

        this.shape_189 = new cjs.Shape();
        this.shape_189.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_189.setTransform(119.725, 30.775);

        this.shape_190 = new cjs.Shape();
        this.shape_190.graphics.f("#FFFFFF").s().p("AgeAuQgIgIAAgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgUAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgMAJQgFAGgBAJQAAAHAEAEQADAFAHAAQAFAAAGgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_190.setTransform(111.5, 30.875);

        this.shape_191 = new cjs.Shape();
        this.shape_191.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgIAAgFgDQgGgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgGAAgFAEg");
        this.shape_191.setTransform(102.2, 32.8);

        this.shape_192 = new cjs.Shape();
        this.shape_192.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_192.setTransform(92.675, 30.875);

        this.shape_193 = new cjs.Shape();
        this.shape_193.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_193.setTransform(85.375, 30.775);

        this.shape_194 = new cjs.Shape();
        this.shape_194.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgHAAgHgDQgFgEgFgFIAAAygAgLg2QgFAEgDAGIAAA0QADAGAFAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgFAAgFAEg");
        this.shape_194.setTransform(77.2, 32.8);

        this.shape_195 = new cjs.Shape();
        this.shape_195.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_195.setTransform(65.625, 36.425);

        this.shape_196 = new cjs.Shape();
        this.shape_196.graphics.f("#FFFFFF").s().p("AgPBJQgIgCgGgDIAFgPIALAEQAGACAGAAQAKAAAFgHQAFgGAAgOIAAgJQgEAFgGAEQgGADgGAAQgRAAgJgNQgJgNAAgTIAAgOQAAgWAJgOQAKgOAQAAQAHAAAGADQAGAEAFAHIABgMIARAAIAABoQAAAVgLALQgKALgTAAQgGAAgIgCgAgNgxQgFAKAAAPIAAAOQAAAMAFAJQAEAIAKAAQAGAAAFgDQAEgEADgFIAAg0QgDgGgEgEQgFgEgGAAQgJAAgFAKg");
        this.shape_196.setTransform(58.875, 32.9);

        this.shape_197 = new cjs.Shape();
        this.shape_197.graphics.f("#FFFFFF").s().p("AASA2IAAhBQAAgOgFgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgUAAIAAhpIASAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_197.setTransform(49.35, 30.775);

        this.shape_198 = new cjs.Shape();
        this.shape_198.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_198.setTransform(42.25, 28.525);

        this.shape_199 = new cjs.Shape();
        this.shape_199.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgFAEgDAHIAABNIgTAAIAAhpIASAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_199.setTransform(35.15, 30.775);

        this.shape_200 = new cjs.Shape();
        this.shape_200.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgEAEgEAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALgBAVIAABBg");
        this.shape_200.setTransform(25.45, 30.775);

        this.shape_201 = new cjs.Shape();
        this.shape_201.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEADgHIAAgVIgQAAQgHAAgFAFg");
        this.shape_201.setTransform(15.9, 30.875);

        this.shape_202 = new cjs.Shape();
        this.shape_202.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_202.setTransform(9, 28.525);

        this.shape_203 = new cjs.Shape();
        this.shape_203.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgJANgRAAQgGAAgGgDQgGgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgHAAgEAEg");
        this.shape_203.setTransform(2.1, 32.8);

        this.shape_204 = new cjs.Shape();
        this.shape_204.graphics.f("#FFFFFF").s().p("AAbBHIg1hpIAAAAIAABpIgUAAIAAiNIAUAAIA1BoIAAAAIAAhoIAUAAIAACNg");
        this.shape_204.setTransform(-13.4, 29.05);

        this.shape_205 = new cjs.Shape();
        this.shape_205.graphics.f("#FFFFFF").s().p("AAaBHQgEgDgBgHIAAgNIAAgNQAAgLgGgIQgFgGgKgBIgaAAIAAA+IgTAAIAAiNIAsAAQATAAALALQALAKAAAUQAAALgFAIQgFAIgKAEQALAEAFAIQAEAIAAANIAAANQAAAHACAFQABAGADADIAAACgAgagEIAYAAQALAAAGgHQAFgGAAgMQAAgNgFgGQgGgHgKAAIgZAAg");
        this.shape_205.setTransform(-24.825, 29.05);

        this.shape_206 = new cjs.Shape();
        this.shape_206.graphics.f("#FFFFFF").s().p("AgqBHIAAiNIAnAAQATAAAMAJQALAKAAATQAAALgFAHQgFAIgJAEQALACAGAIQAGAKAAANQAAAUgLAJQgLALgTAAgAgXA4IAZAAQAKAAAGgGQAGgHAAgMQAAgMgFgHQgFgHgKAAIgbAAgAgXgJIAWAAQAJAAAFgFQAGgHAAgKQAAgNgGgFQgGgGgKAAIgUAAg");
        this.shape_206.setTransform(-36.075, 29.05);

        this.shape_207 = new cjs.Shape();
        this.shape_207.graphics.f("#FFFFFF").s().p("AgfA7QgNgOAAgaIAAgmQAAgZANgOQAMgOAUAAQAVAAALALQALALAAAWIAAABIgTAAQABgPgHgIQgFgHgNAAQgLAAgHAKQgHAKABASIAAAmQgBATAHAKQAHAKALAAQANAAAFgHQAHgHgBgQIATAAIAAAAQABAVgMAMQgLAMgVAAQgTAAgNgOg");
        this.shape_207.setTransform(-47, 29.05);

        this.shape_208 = new cjs.Shape();
        this.shape_208.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_208.setTransform(-58.95, 28.525);

        this.shape_209 = new cjs.Shape();
        this.shape_209.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_209.setTransform(-63.45, 28.525);

        this.shape_210 = new cjs.Shape();
        this.shape_210.graphics.f("#FFFFFF").s().p("AgdAuQgJgIAAgOQAAgPALgJQAKgIASAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_210.setTransform(-70.4, 30.875);

        this.shape_211 = new cjs.Shape();
        this.shape_211.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_211.setTransform(-82.075, 30.775);

        this.shape_212 = new cjs.Shape();
        this.shape_212.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_212.setTransform(-90.45, 30.875);

        this.shape_213 = new cjs.Shape();
        this.shape_213.graphics.f("#FFFFFF").s().p("AgNBNIAAhaIgPAAIAAgOIAPAAIAAgOQgBgRAJgJQAGgJAPAAIAGAAIAHACIgDAOIgDAAIgEgBQgHAAgEAFQgDAGAAAJIAAAOIASAAIAAAOIgSAAIAABag");
        this.shape_213.setTransform(-98.1, 28.425);

        this.shape_214 = new cjs.Shape();
        this.shape_214.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgJANgRAAQgGAAgGgDQgGgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAEACAHAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgHAAgEAEg");
        this.shape_214.setTransform(-110.4, 32.8);

        this.shape_215 = new cjs.Shape();
        this.shape_215.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKAAAOIAAANQAAAOAGAJQAGAKAJAAQALAAAFgKQAGgJgBgOIAAgNQABgOgGgJQgFgKgLAAQgJAAgGAJg");
        this.shape_215.setTransform(-120.3, 30.875);

        this.shape_216 = new cjs.Shape();
        this.shape_216.graphics.f("#FFFFFF").s().p("AASBMIAAhBQAAgNgEgGQgFgHgIAAQgFAAgFAEQgFADgDAGIAABOIgTAAIAAiXIATAAIAAA9QAFgIAGgEQAGgEAIAAQAOAAAHAKQAJALgBAVIAABAg");
        this.shape_216.setTransform(-130, 28.525);

        this.shape_217 = new cjs.Shape();
        this.shape_217.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_217.setTransform(-139.225, 30.875);

        this.shape_218 = new cjs.Shape();
        this.shape_218.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIARAAIACAMQAEgHAGgEQAGgDAHAAQARAAAJAOQAJAOAAAWIAAAOQAAATgJANQgJANgRAAQgHAAgGgDQgFgEgFgFIAAAygAgLg2QgEAEgEAGIAAA0QAEAGAEAEQAEACAHAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgFAAgFAEg");
        this.shape_218.setTransform(-152.55, 32.8);

        this.shape_219 = new cjs.Shape();
        this.shape_219.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_219.setTransform(-162.45, 30.875);

        this.shape_220 = new cjs.Shape();
        this.shape_220.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg9IgOAAIAAgPIAOAAIAAgaIATAAIAAAaIAQAAIAAAPIgQAAIAAA9QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_220.setTransform(-170.475, 29.65);

        this.shape_221 = new cjs.Shape();
        this.shape_221.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_221.setTransform(-177.825, 30.875);

        this.shape_222 = new cjs.Shape();
        this.shape_222.graphics.f("#FFFFFF").s().p("AgVAIIAAgPIArAAIAAAPg");
        this.shape_222.setTransform(-185.075, 30.1);

        this.shape_223 = new cjs.Shape();
        this.shape_223.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_223.setTransform(-192.425, 30.875);

        this.shape_224 = new cjs.Shape();
        this.shape_224.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAIALgBAVIAABBg");
        this.shape_224.setTransform(-201.9, 30.775);

        this.shape_225 = new cjs.Shape();
        this.shape_225.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_225.setTransform(-211.6, 30.875);

        this.shape_226 = new cjs.Shape();
        this.shape_226.graphics.f("#FFFFFF").s().p("AgeAuQgHgIgBgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgTAAIgBgBQAAgMAKgJQAKgKAQAAQAPAAAKAJQAKAKAAARIAAAwIABAMIACALIgUAAIgCgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgIgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAHAAAFgEQAGgEACgHIAAgVIgOAAQgJAAgFAFg");
        this.shape_226.setTransform(205.75, 2.525);

        this.shape_227 = new cjs.Shape();
        this.shape_227.graphics.f("#FFFFFF").s().p("AgPBJQgIgCgGgDIAFgPIALAEQAGACAGAAQAKAAAFgHQAFgGAAgOIAAgJQgEAFgGAEQgGADgGAAQgRAAgJgNQgJgNAAgTIAAgOQAAgWAJgOQAKgOAQAAQAHAAAGADQAGAEAFAHIABgMIARAAIAABoQAAAVgLALQgKALgTAAQgGAAgIgCgAgNgxQgFAKAAAPIAAAOQAAAMAFAJQAEAIAKAAQAGAAAFgDQAEgEADgFIAAg0QgDgGgEgEQgFgEgGAAQgJAAgFAKg");
        this.shape_227.setTransform(191.725, 4.55);

        this.shape_228 = new cjs.Shape();
        this.shape_228.graphics.f("#FFFFFF").s().p("AASA2IAAhBQAAgOgFgGQgEgGgIAAQgFAAgFADQgFAEgDAHIAABNIgTAAIAAhpIASAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_228.setTransform(182.2, 2.425);

        this.shape_229 = new cjs.Shape();
        this.shape_229.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_229.setTransform(175.1, 0.175);

        this.shape_230 = new cjs.Shape();
        this.shape_230.graphics.f("#FFFFFF").s().p("AgcA/QgKgOAAgXIAAgCQAAgZAKgPQAIgPARAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgIgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAGAKAJAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgKAAgEALg");
        this.shape_230.setTransform(167.8, 0.275);

        this.shape_231 = new cjs.Shape();
        this.shape_231.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_231.setTransform(160.9, 0.175);

        this.shape_232 = new cjs.Shape();
        this.shape_232.graphics.f("#FFFFFF").s().p("AgIA1IgfhpIAUAAIASBJIABAIIAAAAIACgIIAShJIAUAAIggBpg");
        this.shape_232.setTransform(154.35, 2.525);

        this.shape_233 = new cjs.Shape();
        this.shape_233.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKAAAOIAAANQAAAOAGAJQAFAKAKAAQALAAAFgKQAFgJAAgOIAAgNQAAgOgFgJQgFgKgLAAQgKAAgFAJg");
        this.shape_233.setTransform(145.1, 2.525);

        this.shape_234 = new cjs.Shape();
        this.shape_234.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_234.setTransform(137.575, 2.425);

        this.shape_235 = new cjs.Shape();
        this.shape_235.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgHAAgHgDQgFgEgFgFIAAAygAgLg2QgFAEgDAGIAAA0QADAGAFAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgKAAQgFAAgFAEg");
        this.shape_235.setTransform(129.4, 4.45);

        this.shape_236 = new cjs.Shape();
        this.shape_236.graphics.f("#FFFFFF").s().p("AgLAYIAGgdIAAgSIARAAIAAATIgMAcg");
        this.shape_236.setTransform(117.825, 8.075);

        this.shape_237 = new cjs.Shape();
        this.shape_237.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_237.setTransform(111.625, 2.525);

        this.shape_238 = new cjs.Shape();
        this.shape_238.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_238.setTransform(102.5512, 2.525);

        this.shape_239 = new cjs.Shape();
        this.shape_239.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgEgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgVAAIAAhpIATAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_239.setTransform(93.15, 2.425);

        this.shape_240 = new cjs.Shape();
        this.shape_240.graphics.f("#FFFFFF").s().p("AgeAuQgIgIAAgOQAAgPALgJQALgIARAAIAOAAIAAgKQAAgKgDgFQgFgGgHAAQgHAAgEAFQgFAFABAHIgUAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgIgJgAgMAJQgFAGgBAJQAAAHAEAEQADAFAHAAQAFAAAGgEQAGgEACgHIAAgVIgPAAQgHAAgFAFg");
        this.shape_240.setTransform(83.6, 2.525);

        this.shape_241 = new cjs.Shape();
        this.shape_241.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAJgPARAAQAGAAAHADQAFADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgJgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAFAKAKAAQAHAAAEgEQAFgDACgGIAAgzQgCgGgFgEQgEgEgHAAQgKAAgEALg");
        this.shape_241.setTransform(73.9, 0.275);

        this.shape_242 = new cjs.Shape();
        this.shape_242.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_242.setTransform(67, 0.175);

        this.shape_243 = new cjs.Shape();
        this.shape_243.graphics.f("#FFFFFF").s().p("AgcArQgJgLABgYIAAg9IATAAIAAA+QAAAQAEAHQAEAGAIAAQAGAAAFgDQAFgDACgHIAAhOIAUAAIAABpIgRAAIgCgOQgDAHgHAFQgHAEgHAAQgPAAgHgLg");
        this.shape_243.setTransform(59.9, 2.625);

        this.shape_244 = new cjs.Shape();
        this.shape_244.graphics.f("#FFFFFF").s().p("AgPBJQgIgCgGgDIAFgPIALAEQAGACAGAAQAKAAAFgHQAFgGAAgOIAAgJQgEAFgGAEQgGADgGAAQgRAAgJgNQgJgNAAgTIAAgOQAAgWAJgOQAKgOAQAAQAHAAAGADQAGAEAFAHIABgMIARAAIAABoQAAAVgLALQgKALgTAAQgGAAgIgCgAgNgxQgFAKAAAPIAAAOQAAAMAFAJQAEAIAKAAQAGAAAFgDQAEgEADgFIAAg0QgDgGgEgEQgFgEgGAAQgJAAgFAKg");
        this.shape_244.setTransform(50.025, 4.55);

        this.shape_245 = new cjs.Shape();
        this.shape_245.graphics.f("#FFFFFF").s().p("AgcBJIgFgBIABgOIADAAIAEABQAFgBAEgFQADgEACgHIAEgLIgihoIAWAAIARBFIACAGIAAAAIAUhLIAVAAIglB5QgDALgGAIQgGAHgMAAIgFgBg");
        this.shape_245.setTransform(36.65, 4.65);

        this.shape_246 = new cjs.Shape();
        this.shape_246.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_246.setTransform(27.8512, 2.525);

        this.shape_247 = new cjs.Shape();
        this.shape_247.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_247.setTransform(21.05, 0.175);

        this.shape_248 = new cjs.Shape();
        this.shape_248.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_248.setTransform(16.55, 0.175);

        this.shape_249 = new cjs.Shape();
        this.shape_249.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJABgOIAAgNQgBgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_249.setTransform(9.45, 2.525);

        this.shape_250 = new cjs.Shape();
        this.shape_250.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgJANgRAAQgHAAgFgDQgGgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAEACAHAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgHAAgEAEg");
        this.shape_250.setTransform(-0.05, 4.45);

        this.shape_251 = new cjs.Shape();
        this.shape_251.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_251.setTransform(-11.7, 0.175);

        this.shape_252 = new cjs.Shape();
        this.shape_252.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_252.setTransform(-18.425, 2.525);

        this.shape_253 = new cjs.Shape();
        this.shape_253.graphics.f("#FFFFFF").s().p("AgIA1IgghpIAVAAIASBJIABAIIAAAAIACgIIAShJIAUAAIgfBpg");
        this.shape_253.setTransform(-27.35, 2.525);

        this.shape_254 = new cjs.Shape();
        this.shape_254.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_254.setTransform(-36.225, 2.525);

        this.shape_255 = new cjs.Shape();
        this.shape_255.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_255.setTransform(-43.1, 0.175);

        this.shape_256 = new cjs.Shape();
        this.shape_256.graphics.f("#FFFFFF").s().p("AgVAIIAAgPIArAAIAAAPg");
        this.shape_256.setTransform(-48.275, 1.75);

        this.shape_257 = new cjs.Shape();
        this.shape_257.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_257.setTransform(-53.4, 0.175);

        this.shape_258 = new cjs.Shape();
        this.shape_258.graphics.f("#FFFFFF").s().p("AgdAuQgIgIAAgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAKAKAAARIAAAwIAAAMIACALIgUAAIgBgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgHgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgFAFg");
        this.shape_258.setTransform(-60.35, 2.525);

        this.shape_259 = new cjs.Shape();
        this.shape_259.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_259.setTransform(-69.85, 2.425);

        this.shape_260 = new cjs.Shape();
        this.shape_260.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJAAgOIAAgNQAAgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_260.setTransform(-79.55, 2.525);

        this.shape_261 = new cjs.Shape();
        this.shape_261.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_261.setTransform(-86.65, 0.175);

        this.shape_262 = new cjs.Shape();
        this.shape_262.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_262.setTransform(-92.075, 1.3);

        this.shape_263 = new cjs.Shape();
        this.shape_263.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgGAFgIAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEADgHIAAgVIgQAAQgHAAgFAFg");
        this.shape_263.setTransform(-99.75, 2.525);

        this.shape_264 = new cjs.Shape();
        this.shape_264.graphics.f("#FFFFFF").s().p("AASA2IAAhBQgBgOgEgGQgDgGgJAAQgFAAgFADQgEAEgDAHIAABNIgVAAIAAhpIATAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_264.setTransform(-109.25, 2.425);

        this.shape_265 = new cjs.Shape();
        this.shape_265.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_265.setTransform(-122.825, 2.525);

        this.shape_266 = new cjs.Shape();
        this.shape_266.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_266.setTransform(-131.625, 2.525);

        this.shape_267 = new cjs.Shape();
        this.shape_267.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_267.setTransform(-139.425, 1.3);

        this.shape_268 = new cjs.Shape();
        this.shape_268.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAGAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgEAFg");
        this.shape_268.setTransform(-147.1, 2.525);

        this.shape_269 = new cjs.Shape();
        this.shape_269.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_269.setTransform(-154.425, 2.425);

        this.shape_270 = new cjs.Shape();
        this.shape_270.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJAAgOIAAgNQAAgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_270.setTransform(-162.8, 2.525);

        this.shape_271 = new cjs.Shape();
        this.shape_271.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgIAAgGgDQgFgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgGAAgFAEg");
        this.shape_271.setTransform(-172.3, 4.45);

        this.shape_272 = new cjs.Shape();
        this.shape_272.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_272.setTransform(-180.025, 2.425);

        this.shape_273 = new cjs.Shape();
        this.shape_273.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgGAKABAOIAAANQgBAOAGAJQAFAKAKAAQAKAAAGgKQAGgJAAgOIAAgNQAAgOgGgJQgGgKgKAAQgKAAgFAJg");
        this.shape_273.setTransform(-188.4, 2.525);

        this.shape_274 = new cjs.Shape();
        this.shape_274.graphics.f("#FFFFFF").s().p("AgbApQgKgNAAgVIAAgNQAAgVAKgNQALgOASAAQAQAAAKALQAKALAAARIAAAAIgSAAQAAgLgFgGQgFgHgIAAQgKAAgFAJQgFAKAAAOIAAANQAAAPAFAJQAFAJAKAAQAIAAAFgGQAFgGAAgJIASAAIAAAAQAAAPgKALQgLAKgPAAQgSAAgLgOg");
        this.shape_274.setTransform(-197.6988, 2.525);

        this.shape_275 = new cjs.Shape();
        this.shape_275.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_275.setTransform(-207.1, 2.425);

        this.shape_276 = new cjs.Shape();
        this.shape_276.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_276.setTransform(-214.2, 0.175);

        this.shape_277 = new cjs.Shape();
        this.shape_277.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAJgPARAAQAGAAAHADQAFADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgJgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAFAKAKAAQAHAAAEgEQAFgDACgGIAAgzQgCgGgFgEQgEgEgHAAQgKAAgEALg");
        this.shape_277.setTransform(173.8, -28.075);

        this.shape_278 = new cjs.Shape();
        this.shape_278.graphics.f("#FFFFFF").s().p("AASA2IAAhBQgBgOgDgGQgFgGgIAAQgFAAgFADQgFAEgDAHIAABNIgTAAIAAhpIASAAIABAQQAEgJAHgEQAGgFAIAAQAOAAAIAKQAHALAAAVIAABBg");
        this.shape_278.setTransform(164.3, -25.925);

        this.shape_279 = new cjs.Shape();
        this.shape_279.graphics.f("#FFFFFF").s().p("AgdAuQgIgIAAgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAQAAQAPAAAKAJQAKAKAAARIAAAwIAAAMIACALIgUAAIgBgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgHgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAGAAAHgEQAFgEADgHIAAgVIgPAAQgJAAgFAFg");
        this.shape_279.setTransform(154.75, -25.825);

        this.shape_280 = new cjs.Shape();
        this.shape_280.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_280.setTransform(141.375, -25.825);

        this.shape_281 = new cjs.Shape();
        this.shape_281.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_281.setTransform(133.875, -27.05);

        this.shape_282 = new cjs.Shape();
        this.shape_282.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_282.setTransform(126.425, -25.825);

        this.shape_283 = new cjs.Shape();
        this.shape_283.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_283.setTransform(117.425, -25.825);

        this.shape_284 = new cjs.Shape();
        this.shape_284.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_284.setTransform(108.725, -25.825);

        this.shape_285 = new cjs.Shape();
        this.shape_285.graphics.f("#FFFFFF").s().p("AgdAuQgJgIAAgOQAAgPALgJQAKgIASAAIAPAAIAAgKQAAgKgEgFQgFgGgHAAQgHAAgEAFQgFAFAAAHIgTAAIAAgBQAAgMAKgJQALgKAOAAQAQAAAKAJQAJAKAAARIAAAwIABAMIADALIgVAAIgBgHIgBgHQgFAHgGAEQgGAFgIAAQgNAAgHgJgAgMAJQgGAGAAAJQABAHADAEQADAFAHAAQAGAAAFgEQAGgEADgHIAAgVIgQAAQgHAAgFAFg");
        this.shape_285.setTransform(99.7, -25.825);

        this.shape_286 = new cjs.Shape();
        this.shape_286.graphics.f("#FFFFFF").s().p("AgJBMIAAiXIATAAIAACXg");
        this.shape_286.setTransform(88.45, -28.175);

        this.shape_287 = new cjs.Shape();
        this.shape_287.graphics.f("#FFFFFF").s().p("AgdAuQgJgIABgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAJAKAAARIAAAwIABAMIACALIgUAAIgBgHIgBgHQgEAHgHAEQgFAFgJAAQgNAAgHgJgAgMAJQgGAGABAJQAAAHADAEQADAFAGAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgEAFg");
        this.shape_287.setTransform(81.5, -25.825);

        this.shape_288 = new cjs.Shape();
        this.shape_288.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_288.setTransform(74.175, -25.925);

        this.shape_289 = new cjs.Shape();
        this.shape_289.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_289.setTransform(66.175, -25.825);

        this.shape_290 = new cjs.Shape();
        this.shape_290.graphics.f("#FFFFFF").s().p("AgdA/QgJgOAAgXIAAgCQAAgZAJgPQAKgPAQAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgRAAIgCgMQgEAHgGAEQgGADgHAAQgQAAgKgOgAgNgFQgFALAAASIAAACQAAAQAFAKQAEAKAKAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgJAAgFALg");
        this.shape_290.setTransform(56.5, -28.075);

        this.shape_291 = new cjs.Shape();
        this.shape_291.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_291.setTransform(47.375, -25.825);

        this.shape_292 = new cjs.Shape();
        this.shape_292.graphics.f("#FFFFFF").s().p("AgNBNIAAhaIgPAAIAAgOIAPAAIAAgOQgBgRAJgJQAGgJAPAAIAGAAIAHACIgDAOIgDAAIgFgBQgGAAgEAFQgDAGAAAJIAAAOIASAAIAAAOIgSAAIAABag");
        this.shape_292.setTransform(39.95, -28.275);

        this.shape_293 = new cjs.Shape();
        this.shape_293.graphics.f("#FFFFFF").s().p("AASBMIAAhBQAAgNgFgGQgDgHgJAAQgFAAgFAEQgEADgDAGIAABOIgVAAIAAiXIAVAAIAAA9QAEgIAGgEQAGgEAHAAQAPAAAIAKQAHALABAVIAABAg");
        this.shape_293.setTransform(27.45, -28.175);

        this.shape_294 = new cjs.Shape();
        this.shape_294.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_294.setTransform(19.425, -27.05);

        this.shape_295 = new cjs.Shape();
        this.shape_295.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_295.setTransform(14.2, -28.175);

        this.shape_296 = new cjs.Shape();
        this.shape_296.graphics.f("#FFFFFF").s().p("AATA1IgThFIAAAAIgSBFIgRAAIgYhpIATAAIAOBGIABAAIAThGIANAAIATBGIABAAIAOhGIATAAIgZBpg");
        this.shape_296.setTransform(5.575, -25.825);

        this.shape_297 = new cjs.Shape();
        this.shape_297.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_297.setTransform(-9.525, -25.825);

        this.shape_298 = new cjs.Shape();
        this.shape_298.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_298.setTransform(-18.325, -25.825);

        this.shape_299 = new cjs.Shape();
        this.shape_299.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_299.setTransform(-26.125, -27.05);

        this.shape_300 = new cjs.Shape();
        this.shape_300.graphics.f("#FFFFFF").s().p("AgdAuQgIgIAAgOQAAgPAKgJQAKgIASAAIAPAAIAAgKQAAgKgFgFQgEgGgHAAQgHAAgEAFQgEAFgBAHIgTAAIAAgBQAAgMAKgJQAKgKAPAAQAQAAAKAJQAKAKAAARIAAAwIAAAMIACALIgUAAIgBgHIgBgHQgFAHgGAEQgFAFgJAAQgNAAgHgJgAgNAJQgEAGAAAJQAAAHADAEQAEAFAFAAQAGAAAHgEQAFgEADgHIAAgVIgQAAQgIAAgFAFg");
        this.shape_300.setTransform(-33.8, -25.825);

        this.shape_301 = new cjs.Shape();
        this.shape_301.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_301.setTransform(-41.125, -25.925);

        this.shape_302 = new cjs.Shape();
        this.shape_302.graphics.f("#FFFFFF").s().p("AgPBJQgIgCgGgDIAFgPIALAEQAGACAGAAQAKAAAFgHQAFgGAAgOIAAgJQgEAFgGAEQgGADgGAAQgRAAgJgNQgJgNAAgTIAAgOQAAgWAJgOQAKgOAQAAQAHAAAGADQAGAEAFAHIABgMIARAAIAABoQAAAVgLALQgKALgTAAQgGAAgIgCgAgNgxQgFAKAAAPIAAAOQAAAMAFAJQAEAIAKAAQAGAAAFgDQAEgEADgFIAAg0QgDgGgEgEQgFgEgGAAQgJAAgFAKg");
        this.shape_302.setTransform(-49.675, -23.8);

        this.shape_303 = new cjs.Shape();
        this.shape_303.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_303.setTransform(-58.825, -25.825);

        this.shape_304 = new cjs.Shape();
        this.shape_304.graphics.f("#FFFFFF").s().p("AgGA8QgGgIAAgQIAAg+IgOAAIAAgOIAOAAIAAgaIATAAIAAAaIAQAAIAAAOIgQAAIAAA+QAAAJACADQADAEAEAAIAEgBIAFAAIACANIgIACIgIABQgLAAgGgHg");
        this.shape_304.setTransform(-66.625, -27.05);

        this.shape_305 = new cjs.Shape();
        this.shape_305.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgFgGgIAAQgFAAgFADQgEAEgEAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALgBAVIAABBg");
        this.shape_305.setTransform(-74.45, -25.925);

        this.shape_306 = new cjs.Shape();
        this.shape_306.graphics.f("#FFFFFF").s().p("AgJBMIAAhoIATAAIAABogAgJg4IAAgTIATAAIAAATg");
        this.shape_306.setTransform(-81.55, -28.175);

        this.shape_307 = new cjs.Shape();
        this.shape_307.graphics.f("#FFFFFF").s().p("AgXA2IAAhpIASAAIACAQQADgIAEgFQAGgFAHAAIAEABIADAAIgCASIgKgBQgGAAgEAEQgDAEgCAGIAABLg");
        this.shape_307.setTransform(-90.825, -25.925);

        this.shape_308 = new cjs.Shape();
        this.shape_308.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_308.setTransform(-98.825, -25.825);

        this.shape_309 = new cjs.Shape();
        this.shape_309.graphics.f("#FFFFFF").s().p("AgcA/QgKgOAAgXIAAgCQAAgZAKgPQAIgPARAAQAHAAAFADQAGADAEAHIAAg6IAUAAIAACXIgSAAIgBgMQgEAHgGAEQgGADgHAAQgRAAgIgOgAgNgFQgFALAAASIAAACQAAAQAEAKQAGAKAJAAQAGAAAFgEQAEgDADgGIAAgzQgDgGgEgEQgFgEgGAAQgKAAgEALg");
        this.shape_309.setTransform(-108.5, -28.075);

        this.shape_310 = new cjs.Shape();
        this.shape_310.graphics.f("#FFFFFF").s().p("AARA2IAAhBQAAgOgDgGQgEgGgJAAQgFAAgFADQgFAEgDAHIAABNIgUAAIAAhpIATAAIABAQQAFgJAGgEQAGgFAIAAQAOAAAIAKQAIALAAAVIAABBg");
        this.shape_310.setTransform(-118, -25.925);

        this.shape_311 = new cjs.Shape();
        this.shape_311.graphics.f("#FFFFFF").s().p("AgdApQgLgNAAgVIAAgNQAAgVALgNQALgOASAAQATAAALAOQALANAAAVIAAANQAAAVgLANQgLAOgTAAQgSAAgLgOgAgPgeQgFAKgBAOIAAANQABAOAFAJQAGAKAJAAQAKAAAGgKQAFgJAAgOIAAgNQAAgOgFgJQgGgKgKAAQgJAAgGAJg");
        this.shape_311.setTransform(-127.7, -25.825);

        this.shape_312 = new cjs.Shape();
        this.shape_312.graphics.f("#FFFFFF").s().p("AgmBKIAAiRIASAAIABAMQAEgHAGgEQAGgDAHAAQAQAAAKAOQAJAOAAAWIAAAOQAAATgJANQgKANgPAAQgIAAgGgDQgFgEgEgFIAAAygAgLg2QgEAEgDAGIAAA0QADAGAEAEQAFACAGAAQAJAAAFgIQAFgJAAgMIAAgOQAAgPgFgKQgFgKgJAAQgGAAgFAEg");
        this.shape_312.setTransform(-137.2, -23.9);

        this.shape_313 = new cjs.Shape();
        this.shape_313.graphics.f("#FFFFFF").s().p("AgaAtQgKgKAAgNIABgBIASAAQABAKAFAFQAFAEAHAAQAIAAAEgDQAEgEAAgHQAAgGgEgEQgEgEgKgFQgQgHgIgGQgIgHAAgMQAAgMAKgIQAJgJAPAAQAQAAAKAJQAJAKAAANIAAABIgTAAQAAgIgEgFQgFgFgHAAQgHAAgEAEQgEAEAAAGQAAAGAEADQADAEALAFQARAGAIAHQAIAHAAAMQAAANgKAJQgKAIgQAAQgRAAgKgKg");
        this.shape_313.setTransform(-146.625, -25.825);

        this.shape_314 = new cjs.Shape();
        this.shape_314.graphics.f("#FFFFFF").s().p("AgaAqQgLgNAAgVIAAgOQAAgVALgNQALgOAQAAQASAAAKAMQAJANAAAUIAAANIg4AAIAAAEQAAAOAHAJQAFAJALAAQAIAAAGgDQAFgCAGgFIAGAMQgFAGgIADQgJAEgLAAQgSAAgLgNgAgMgeQgFAIgBANIAlAAIAAgEQAAgLgFgIQgEgHgJAAQgIAAgFAJg");
        this.shape_314.setTransform(-155.425, -25.825);

        this.shape_315 = new cjs.Shape();
        this.shape_315.graphics.f("#FFFFFF").s().p("AAaBHQgEgDgBgHIAAgNIAAgNQAAgLgGgIQgFgGgKgBIgaAAIAAA+IgTAAIAAiNIAsAAQATAAALALQALAKAAAUQAAALgFAIQgFAIgKAEQALAEAFAIQAEAIAAANIAAANQAAAHACAFQABAGADADIAAACgAgagEIAYAAQALAAAGgHQAFgGAAgMQAAgNgFgGQgGgHgKAAIgZAAg");
        this.shape_315.setTransform(-165.375, -27.65);

        this.shape_316 = new cjs.Shape();
        this.shape_316.graphics.f("#FFFFFF").s().p("AAbBHIg1hpIgBAAIAABpIgTAAIAAiNIATAAIA1BoIABAAIAAhoIAUAAIAACNg");
        this.shape_316.setTransform(-177.5, -27.65);

        this.shape_317 = new cjs.Shape();
        this.shape_317.graphics.f("#FFFFFF").s().p("AAaBHQgEgDgBgHIAAgNIAAgNQAAgLgGgIQgFgGgKgBIgaAAIAAA+IgTAAIAAiNIAsAAQATAAALALQALAKAAAUQAAALgFAIQgFAIgKAEQALAEAFAIQAEAIAAANIAAANQAAAHACAFQABAGADADIAAACgAgagEIAYAAQALAAAGgHQAFgGAAgMQAAgNgFgGQgGgHgKAAIgZAAg");
        this.shape_317.setTransform(-188.925, -27.65);

        this.shape_318 = new cjs.Shape();
        this.shape_318.graphics.f("#FFFFFF").s().p("AgqBHIAAiNIAnAAQATAAAMAJQALAKAAATQAAALgFAHQgFAIgJAEQALACAGAIQAGAKAAANQAAAUgLAJQgLALgTAAgAgXA4IAZAAQAKAAAGgGQAGgHAAgMQAAgMgFgHQgFgHgKAAIgbAAgAgXgJIAWAAQAJAAAFgFQAGgHAAgKQAAgNgGgFQgGgGgKAAIgUAAg");
        this.shape_318.setTransform(-200.175, -27.65);

        this.shape_319 = new cjs.Shape();
        this.shape_319.graphics.f("#FFFFFF").s().p("AgfA7QgNgOAAgaIAAgmQAAgZANgOQAMgOAUAAQAVAAALALQAMALAAAWIAAABIgTAAQAAgPgHgIQgGgHgMAAQgLAAgHAKQgGAJgBATIAAAmQABATAGAKQAHAKALAAQANAAAFgHQAHgHAAgQIATAAIAAAAQgBAVgKAMQgMAMgVAAQgTAAgNgOg");
        this.shape_319.setTransform(-211.1, -27.65);

        this.shape_320 = new cjs.Shape();
        this.shape_320.graphics.f("#FFFFFF").s().p("AgPANIAFgYIAaAAIgFAYg");
        this.shape_320.setTransform(63.525, -78.5);

        this.shape_321 = new cjs.Shape();
        this.shape_321.graphics.f("#FFFFFF").s().p("AgYA6QgGgHAEgSIAKg0IgLAAIAEgVIALAAIAGgaIAaAAIgFAaIAOAAIgEAVIgOAAIgLA0QAAAGAAADQAAABABAAQAAABABAAQAAAAABAAQAAABABAAIAEAAIAEgBIgCAUIgJACIgHABQgNAAgFgJg");
        this.shape_321.setTransform(59.3319, -83.75);

        this.shape_322 = new cjs.Shape();
        this.shape_322.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_322.setTransform(50.6092, -82.625);

        this.shape_323 = new cjs.Shape();
        this.shape_323.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_323.setTransform(41.8299, -82.525);

        this.shape_324 = new cjs.Shape();
        this.shape_324.graphics.f("#FFFFFF").s().p("AAbA2IANg/QACgNgBgEQgCgFgFAAQgDAAgDADQgEACgDAEIAAAEIgBAEIgNBEIgbAAIANg/QACgNgBgEQgBgFgGAAQgDAAgDACIgGAGIgPBNIgcAAIAWhpIAZAAIgBAMQAFgHAHgDQAGgEAHAAQAHAAAFAEQAFAEABAJQAGgIAHgFQAHgEAIAAQAMAAAFALQAFALgEAWIgNA/g");
        this.shape_324.setTransform(29.9889, -82.625);

        this.shape_325 = new cjs.Shape();
        this.shape_325.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_325.setTransform(18.8799, -82.525);

        this.shape_326 = new cjs.Shape();
        this.shape_326.graphics.f("#FFFFFF").s().p("AgkBJQgHgDgGgDIAJgWQAFAEAFACQAFABAHAAQAHAAAGgHQAFgFACgLIAAgDQgDAFgFACQgFACgFAAQgPAAgGgQQgIgPAFgWIABgCQAFgbAKgOQALgNAPAAQAHAAAFADQAFADADAGIAEgKIAYAAIgVBlQgEAWgOAMQgNAMgVAAQgGAAgHgCgAgEgsQgFAJgDAPIAAACQgDAOABAIQABAJAHgBQAEAAACgCQAEgCADgFIAKgxQgBgDgDgCQgDgBgDAAQgHAAgEAIg");
        this.shape_326.setTransform(9.625, -80.5);

        this.shape_327 = new cjs.Shape();
        this.shape_327.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_327.setTransform(0.3893, -82.525);

        this.shape_328 = new cjs.Shape();
        this.shape_328.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_328.setTransform(-8.8908, -82.625);

        this.shape_329 = new cjs.Shape();
        this.shape_329.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_329.setTransform(-17.9107, -82.525);

        this.shape_330 = new cjs.Shape();
        this.shape_330.graphics.f("#FFFFFF").s().p("AAbA2IANg/QACgNgBgEQgCgFgFAAQgDAAgDADQgEACgDAEIAAAEIgBAEIgNBEIgbAAIANg/QACgNgBgEQgBgFgGAAQgDAAgDACIgGAGIgPBNIgcAAIAWhpIAZAAIgBAMQAFgHAHgDQAGgEAHAAQAHAAAFAEQAFAEABAJQAGgIAHgFQAHgEAIAAQAMAAAFALQAFALgEAWIgNA/g");
        this.shape_330.setTransform(-29.5611, -82.625);

        this.shape_331 = new cjs.Shape();
        this.shape_331.graphics.f("#FFFFFF").s().p("AgYA6QgGgHAEgSIAKg0IgLAAIAEgVIALAAIAGgaIAaAAIgFAaIAOAAIgEAVIgOAAIgLA0QAAAGAAADQAAABABAAQAAABABAAQAAAAABAAQAAABABAAIAEAAIAEgBIgCAUIgJACIgHABQgNAAgFgJg");
        this.shape_331.setTransform(-43.0181, -83.75);

        this.shape_332 = new cjs.Shape();
        this.shape_332.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_332.setTransform(-51.7408, -82.625);

        this.shape_333 = new cjs.Shape();
        this.shape_333.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_333.setTransform(-60.5201, -82.525);

        this.shape_334 = new cjs.Shape();
        this.shape_334.graphics.f("#FFFFFF").s().p("AgeA1IgKhpIAcAAIABBBIAAAHIAAAAIADgHIAVhBIAcAAIgsBpg");
        this.shape_334.setTransform(-68.5, -82.525);

        this.shape_335 = new cjs.Shape();
        this.shape_335.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_335.setTransform(-77.9701, -82.525);

        this.shape_336 = new cjs.Shape();
        this.shape_336.graphics.f("#FFFFFF").s().p("AguA+QgHgQAFgXIABgCQAFgaAKgNQALgNAQAAQAFAAAEACQAFADADAFIALg3IAcAAIgfCXIgXAAIABgJQgFAFgGADQgFADgGAAQgPAAgHgPgAgMgBQgFAIgDAOIAAACQgDAPABAJQABAIAHAAQAEAAADgDQAEgCADgFIAIgvQgBgEgCgBQgDgCgCAAQgHAAgFAIg");
        this.shape_336.setTransform(-90.7292, -84.775);

        this.shape_337 = new cjs.Shape();
        this.shape_337.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIAAAAQAEgHAFgEQAFgEAGAAIAEABIADAAIgIAaIgKAAQgDAAgEACQgDACgCAEIgOBIg");
        this.shape_337.setTransform(-98.95, -82.625);

        this.shape_338 = new cjs.Shape();
        this.shape_338.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_338.setTransform(-107.0107, -82.525);

        this.shape_339 = new cjs.Shape();
        this.shape_339.graphics.f("#FFFFFF").s().p("AgsA1IADgRIAzhCIgkAAIAEgWIBDAAIgDAQIgzBDIAmAAIgEAWg");
        this.shape_339.setTransform(-115.525, -82.525);

        this.shape_340 = new cjs.Shape();
        this.shape_340.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_340.setTransform(-124.2607, -82.525);

        this.shape_341 = new cjs.Shape();
        this.shape_341.graphics.f("#FFFFFF").s().p("AAEBMIANg+QACgOgBgEQgCgEgGAAQgDAAgEACIgEAEIgQBOIgcAAIAfiXIAaAAIgLA5IAAABQAFgHAGgDQAGgDAHAAQAMAAAGALQAGALgFAWIgMA+g");
        this.shape_341.setTransform(-133.6036, -84.875);

        this.shape_342 = new cjs.Shape();
        this.shape_342.graphics.f("#FFFFFF").s().p("AgZALIAFgVIAuAAIgFAVg");
        this.shape_342.setTransform(-141.375, -83.35);

        this.shape_343 = new cjs.Shape();
        this.shape_343.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_343.setTransform(-146.975, -84.875);

        this.shape_344 = new cjs.Shape();
        this.shape_344.graphics.f("#FFFFFF").s().p("AgYA6QgGgHAEgSIAKg0IgLAAIAEgVIALAAIAGgaIAaAAIgFAaIAOAAIgEAVIgOAAIgLA0QAAAGAAADQAAABABAAQAAABABAAQAAAAABAAQAAABABAAIAEAAIAEgBIgCAUIgJACIgHABQgNAAgFgJg");
        this.shape_344.setTransform(-152.2681, -83.75);

        this.shape_345 = new cjs.Shape();
        this.shape_345.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_345.setTransform(-157.875, -84.875);

        this.shape_346 = new cjs.Shape();
        this.shape_346.graphics.f("#FFFFFF").s().p("AgqArQgGgLAFgWIANg/IAbAAIgNA/QgCAMABAFQACAFAFAAQAEAAADgCQADgCADgDIAPhOIAcAAIgVBpIgYAAIAAgLQgEAGgGADQgHAEgHAAQgNAAgGgLg");
        this.shape_346.setTransform(-165.1464, -82.425);

        this.shape_347 = new cjs.Shape();
        this.shape_347.graphics.f("#FFFFFF").s().p("AAbA2IANg/QACgNgBgEQgCgFgFAAQgDAAgDADQgEACgDAEIAAAEIgBAEIgNBEIgbAAIANg/QACgNgBgEQgBgFgGAAQgDAAgDACIgGAGIgPBNIgcAAIAWhpIAZAAIgBAMQAFgHAHgDQAGgEAHAAQAHAAAFAEQAFAEABAJQAGgIAHgFQAHgEAIAAQAMAAAFALQAFALgEAWIgNA/g");
        this.shape_347.setTransform(-177.5111, -82.625);

        this.shape_348 = new cjs.Shape();
        this.shape_348.graphics.f("#FFFFFF").s().p("AguA+QgHgQAFgXIABgCQAFgaAKgNQALgNAQAAQAFAAAEACQAFADADAFIALg3IAcAAIgfCXIgXAAIABgJQgFAFgGADQgFADgGAAQgPAAgHgPgAgMgBQgFAIgDAOIAAACQgDAPABAJQABAIAHAAQAEAAADgDQAEgCADgFIAIgvQgBgEgCgBQgDgCgCAAQgHAAgFAIg");
        this.shape_348.setTransform(-192.5292, -84.775);

        this.shape_349 = new cjs.Shape();
        this.shape_349.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_349.setTransform(-202.9408, -82.625);

        this.shape_350 = new cjs.Shape();
        this.shape_350.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_350.setTransform(-211.9607, -82.525);

        this.shape_351 = new cjs.Shape();
        this.shape_351.graphics.f("#FFFFFF").s().p("AgkBJQgHgDgGgDIAJgWQAFAEAFACQAFABAHAAQAHAAAGgHQAFgFACgLIAAgDQgDAFgFACQgFACgFAAQgPAAgGgQQgIgPAFgWIABgCQAFgbAKgOQALgNAPAAQAHAAAFADQAFADADAGIAEgKIAYAAIgVBlQgEAWgOAMQgNAMgVAAQgGAAgHgCgAgEgsQgFAJgDAPIAAACQgDAOABAIQABAJAHgBQAEAAACgCQAEgCADgFIAKgxQgBgDgDgCQgDgBgDAAQgHAAgEAIg");
        this.shape_351.setTransform(189.125, -108.85);

        this.shape_352 = new cjs.Shape();
        this.shape_352.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_352.setTransform(179.5092, -110.975);

        this.shape_353 = new cjs.Shape();
        this.shape_353.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_353.setTransform(173.225, -113.225);

        this.shape_354 = new cjs.Shape();
        this.shape_354.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIAAAAQAFgHAEgEQAFgEAGAAIAEABIADAAIgIAaIgKAAQgEAAgDACQgDACgCAEIgOBIg");
        this.shape_354.setTransform(167.55, -110.975);

        this.shape_355 = new cjs.Shape();
        this.shape_355.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_355.setTransform(159.4893, -110.875);

        this.shape_356 = new cjs.Shape();
        this.shape_356.graphics.f("#FFFFFF").s().p("AAEBMIANg+QACgOgBgEQgCgEgGAAQgDAAgEACIgEAEIgQBOIgcAAIAfiXIAaAAIgLA5IAAABQAFgHAGgDQAGgDAHAAQAMAAAGALQAGALgFAWIgMA+g");
        this.shape_356.setTransform(150.1464, -113.225);

        this.shape_357 = new cjs.Shape();
        this.shape_357.graphics.f("#FFFFFF").s().p("AghAsQgJgLACgNIABAAIAZAAQgBAIADAEQACADAGAAQAEAAADgCQADgDABgFQABgFgDgDIgKgIQgOgGgHgGQgGgIACgMQACgNALgJQALgJAPAAQARAAAJAKQAIAJgCAOIgBABIgaAAQABgHgCgEQgCgDgFAAQgDAAgDADQgDADgBAEQgBAFADADIAKAHQAPAGAGAHQAHAIgCAMQgDAOgLAJQgLAIgQAAQgRAAgJgLg");
        this.shape_357.setTransform(141.5344, -110.875);

        this.shape_358 = new cjs.Shape();
        this.shape_358.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_358.setTransform(128.5393, -110.875);

        this.shape_359 = new cjs.Shape();
        this.shape_359.graphics.f("#FFFFFF").s().p("AgYA6QgGgHAEgSIAKg0IgLAAIAEgVIALAAIAGgaIAaAAIgFAaIAOAAIgEAVIgOAAIgLA0QAAAGAAADQAAABABAAQAAABABAAQAAAAABAAQAAABABAAIAEAAIAEgBIgCAUIgJACIgHABQgNAAgFgJg");
        this.shape_359.setTransform(121.8319, -112.1);

        this.shape_360 = new cjs.Shape();
        this.shape_360.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_360.setTransform(113.4893, -110.875);

        this.shape_361 = new cjs.Shape();
        this.shape_361.graphics.f("#FFFFFF").s().p("AguA+QgHgQAFgXIABgCQAFgaAKgNQALgNAQAAQAFAAAEACQAFADADAFIALg3IAcAAIgfCXIgXAAIABgJQgFAFgGADQgFADgGAAQgPAAgHgPgAgMgBQgFAIgDAOIAAACQgDAPABAJQABAIAHAAQAEAAADgDQAEgCADgFIAIgvQgBgEgCgBQgDgCgCAAQgHAAgFAIg");
        this.shape_361.setTransform(105.2208, -113.125);

        this.shape_362 = new cjs.Shape();
        this.shape_362.graphics.f("#FFFFFF").s().p("AgYA6QgGgHAEgSIAKg0IgLAAIAEgVIALAAIAGgaIAaAAIgFAaIAOAAIgEAVIgOAAIgLA0QAAAGAAADQAAABABAAQAAABABAAQAAAAABAAQAAABABAAIAEAAIAEgBIgCAUIgJACIgHABQgNAAgFgJg");
        this.shape_362.setTransform(93.0819, -112.1);

        this.shape_363 = new cjs.Shape();
        this.shape_363.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_363.setTransform(84.3592, -110.975);

        this.shape_364 = new cjs.Shape();
        this.shape_364.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_364.setTransform(75.5799, -110.875);

        this.shape_365 = new cjs.Shape();
        this.shape_365.graphics.f("#FFFFFF").s().p("AguA+QgHgQAFgXIABgCQAFgaAKgNQALgNAQAAQAFAAAEACQAFADADAFIALg3IAcAAIgfCXIgXAAIABgJQgFAFgGADQgFADgGAAQgPAAgHgPgAgMgBQgFAIgDAOIAAACQgDAPABAJQABAIAHAAQAEAAADgDQAEgCADgFIAIgvQgBgEgCgBQgDgCgCAAQgHAAgFAIg");
        this.shape_365.setTransform(67.1208, -113.125);

        this.shape_366 = new cjs.Shape();
        this.shape_366.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_366.setTransform(59.825, -113.225);

        this.shape_367 = new cjs.Shape();
        this.shape_367.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_367.setTransform(52.7419, -110.875);

        this.shape_368 = new cjs.Shape();
        this.shape_368.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_368.setTransform(43.4092, -110.975);

        this.shape_369 = new cjs.Shape();
        this.shape_369.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_369.setTransform(37.125, -113.225);

        this.shape_370 = new cjs.Shape();
        this.shape_370.graphics.f("#FFFFFF").s().p("AgcBkIgDgNQALgKAKgVQAKgWAFggIAAgCQAGgigEgVQgDgUgHgKIAGgOIABAAQAOAIAJAbQAIAcgFAkIgBABQgGAmgRAbQgQAagRAIg");
        this.shape_370.setTransform(26.133, -111.275);

        this.shape_371 = new cjs.Shape();
        this.shape_371.graphics.f("#FFFFFF").s().p("AAGBHIgVhYIAAAAIgSBYIgcAAIAdiNIAbAAIAVBXIABAAIARhXIAcAAIgdCNg");
        this.shape_371.setTransform(18.45, -112.7);

        this.shape_372 = new cjs.Shape();
        this.shape_372.graphics.f("#FFFFFF").s().p("AAMBHQgDgDABgHIACgPIACgJQACgMgDgFQgDgFgHAAIgSAAIgLA4IgcAAIAdiNIAsAAQASAAAKALQAKALgEAUQgCAMgGAHQgHAJgKADQAJAEADAIQAEAJgDAMIgCAKIgBAOQAAAGADADIgBACgAgKgHIAQAAQAHABAFgGQAFgFACgKQACgKgCgGQgDgFgIAAIgQAAg");
        this.shape_372.setTransform(6.8071, -112.7);

        this.shape_373 = new cjs.Shape();
        this.shape_373.graphics.f("#FFFFFF").s().p("Ag1BHIAdiNIAnAAQAUAAALAKQAKAJgEAUQgCAKgGAIQgFAIgKADQALACADAJQADAIgCANQgFAVgNAKQgNAKgUAAgAgVAxIASAAQAHAAAFgFQAFgFACgJQACgMgCgEQgCgGgIAAIgSAAgAgJgKIANAAQAIgBAFgEQAFgFACgJQACgKgDgFQgDgEgJAAIgMAAg");
        this.shape_373.setTransform(-3.9948, -112.7);

        this.shape_374 = new cjs.Shape();
        this.shape_374.graphics.f("#FFFFFF").s().p("AgqA4QgMgQAFgaIAGgbQAGgdAOgPQAOgPAUAAQAUAAALAOQALAOgDAXIgbAAQACgOgDgIQgEgHgKAAQgIAAgGAKQgGAKgDARIgGAbQgEASAEAJQADAKAKAAQAIAAAFgHQAFgHAEgOIAbAAIAAAAQgGAZgNAMQgMANgUAAQgUAAgMgRg");
        this.shape_374.setTransform(-14.0414, -112.7);

        this.shape_375 = new cjs.Shape();
        this.shape_375.graphics.f("#FFFFFF").s().p("AgDBkQgOgIgIgaQgIgbAGgmIAAgBQAGgnARgaQAPgaARgIIABAAIACAOQgLAJgKAWQgKAVgEAhIgBACQgFAhADAVQADAVAIAKIgGANg");
        this.shape_375.setTransform(-22.0036, -111.275);

        this.shape_376 = new cjs.Shape();
        this.shape_376.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIABAAQAEgHAFgEQAEgEAGAAIADABIAEAAIgIAaIgKAAQgEAAgDACQgDACgCAEIgNBIg");
        this.shape_376.setTransform(-33, -110.975);

        this.shape_377 = new cjs.Shape();
        this.shape_377.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_377.setTransform(-41.0607, -110.875);

        this.shape_378 = new cjs.Shape();
        this.shape_378.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_378.setTransform(-49.7201, -110.875);

        this.shape_379 = new cjs.Shape();
        this.shape_379.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_379.setTransform(-56.075, -113.225);

        this.shape_380 = new cjs.Shape();
        this.shape_380.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_380.setTransform(-63.1581, -110.875);

        this.shape_381 = new cjs.Shape();
        this.shape_381.graphics.f("#FFFFFF").s().p("AgqArQgGgLAFgWIANg/IAbAAIgNA/QgCAMABAFQACAFAFAAQAEAAADgCQADgCADgDIAPhOIAcAAIgVBpIgYAAIAAgLQgEAGgGADQgHAEgHAAQgNAAgGgLg");
        this.shape_381.setTransform(-71.8964, -110.775);

        this.shape_382 = new cjs.Shape();
        this.shape_382.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_382.setTransform(-81.8908, -110.975);

        this.shape_383 = new cjs.Shape();
        this.shape_383.graphics.f("#FFFFFF").s().p("AguA+QgHgQAFgXIABgCQAFgaAKgNQALgNAQAAQAFAAAEACQAFADADAFIALg3IAcAAIgfCXIgXAAIABgJQgFAFgGADQgFADgGAAQgPAAgHgPgAgMgBQgFAIgDAOIAAACQgDAPABAJQABAIAHAAQAEAAADgDQAEgCADgFIAIgvQgBgEgCgBQgDgCgCAAQgHAAgFAIg");
        this.shape_383.setTransform(-94.5792, -113.125);

        this.shape_384 = new cjs.Shape();
        this.shape_384.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_384.setTransform(-104.9908, -110.975);

        this.shape_385 = new cjs.Shape();
        this.shape_385.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_385.setTransform(-114.0107, -110.875);

        this.shape_386 = new cjs.Shape();
        this.shape_386.graphics.f("#FFFFFF").s().p("AgUAbIAMgeIAFgXIAYAAIgFAYIgTAdg");
        this.shape_386.setTransform(-126.2, -105.275);

        this.shape_387 = new cjs.Shape();
        this.shape_387.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_387.setTransform(-128.925, -113.225);

        this.shape_388 = new cjs.Shape();
        this.shape_388.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_388.setTransform(-136.4107, -110.875);

        this.shape_389 = new cjs.Shape();
        this.shape_389.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_389.setTransform(-144.9081, -110.875);

        this.shape_390 = new cjs.Shape();
        this.shape_390.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_390.setTransform(-151.125, -113.225);

        this.shape_391 = new cjs.Shape();
        this.shape_391.graphics.f("#FFFFFF").s().p("AgkBJQgHgDgGgDIAJgWQAFAEAFACQAFABAHAAQAHAAAGgHQAFgFACgLIAAgDQgDAFgFACQgFACgFAAQgPAAgGgQQgIgPAFgWIABgCQAFgbAKgOQALgNAPAAQAHAAAFADQAFADADAGIAEgKIAYAAIgVBlQgEAWgOAMQgNAMgVAAQgGAAgHgCgAgEgsQgFAJgDAPIAAACQgDAOABAIQABAJAHgBQAEAAACgCQAEgCADgFIAKgxQgBgDgDgCQgDgBgDAAQgHAAgEAIg");
        this.shape_391.setTransform(-158.775, -108.85);

        this.shape_392 = new cjs.Shape();
        this.shape_392.graphics.f("#FFFFFF").s().p("AgkApQgJgNAEgVIACgNQADgWAOgNQAMgNASAAQAUAAAIAOQAKANgEAVIgCANQgDAWgNANQgNANgTAAQgSAAgKgOgAgGgYQgEAHgBALIgCANQgDAMACAHQACAHAIAAQAGAAAFgHQADgHADgMIACgNQACgMgCgHQgCgHgIAAQgGAAgFAIg");
        this.shape_392.setTransform(-168.15, -110.875);

        this.shape_393 = new cjs.Shape();
        this.shape_393.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_393.setTransform(-174.675, -113.225);

        this.shape_394 = new cjs.Shape();
        this.shape_394.graphics.f("#FFFFFF").s().p("AgjApQgJgNADgVIACgNQADgWANgNQANgNATAAQASAAAJAOQAJANgDAVIgCANQgDAWgNANQgNANgTAAQgSAAgJgOgAgGgYQgEAHgBALIgDANQgCAMACAHQACAHAIAAQAGAAAFgHQADgHACgMIADgNQACgMgCgHQgDgHgHAAQgGAAgFAIg");
        this.shape_394.setTransform(-182.3, -110.875);

        this.shape_395 = new cjs.Shape();
        this.shape_395.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_395.setTransform(-188.825, -113.225);

        this.shape_396 = new cjs.Shape();
        this.shape_396.graphics.f("#FFFFFF").s().p("AguA+QgHgQAFgXIABgCQAFgaAKgNQALgNAQAAQAFAAAEACQAFADADAFIALg3IAcAAIgfCXIgXAAIABgJQgFAFgGADQgFADgGAAQgPAAgHgPgAgMgBQgFAIgDAOIAAACQgDAPABAJQABAIAHAAQAEAAADgDQAEgCADgFIAIgvQgBgEgCgBQgDgCgCAAQgHAAgFAIg");
        this.shape_396.setTransform(-195.6792, -113.125);

        this.shape_397 = new cjs.Shape();
        this.shape_397.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_397.setTransform(-205.7107, -110.875);

        this.shape_398 = new cjs.Shape();
        this.shape_398.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIAAAAQAEgHAFgEQAFgEAGAAIAEABIADAAIgIAaIgKAAQgDAAgEACQgDACgCAEIgOBIg");
        this.shape_398.setTransform(-212.8, -110.975);

        this.shape_399 = new cjs.Shape();
        this.shape_399.graphics.f("#FFFFFF").s().p("AgTAbIALgeIAFgXIAXAAIgEAYIgTAdg");
        this.shape_399.setTransform(177.25, -133.625);

        this.shape_400 = new cjs.Shape();
        this.shape_400.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_400.setTransform(174.525, -141.575);

        this.shape_401 = new cjs.Shape();
        this.shape_401.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_401.setTransform(167.0393, -139.225);

        this.shape_402 = new cjs.Shape();
        this.shape_402.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_402.setTransform(158.5419, -139.225);

        this.shape_403 = new cjs.Shape();
        this.shape_403.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_403.setTransform(152.325, -141.575);

        this.shape_404 = new cjs.Shape();
        this.shape_404.graphics.f("#FFFFFF").s().p("AgkBJQgHgDgGgDIAJgWQAFAEAFACQAFABAHAAQAHAAAGgHQAFgFACgLIAAgDQgDAFgFACQgFACgFAAQgPAAgGgQQgIgPAFgWIABgCQAFgbAKgOQALgNAPAAQAHAAAFADQAFADADAGIAEgKIAYAAIgVBlQgEAWgOAMQgNAMgVAAQgGAAgHgCgAgEgsQgFAJgDAPIAAACQgDAOABAIQABAJAHgBQAEAAACgCQAEgCADgFIAKgxQgBgDgDgCQgDgBgDAAQgHAAgEAIg");
        this.shape_404.setTransform(144.675, -137.2);

        this.shape_405 = new cjs.Shape();
        this.shape_405.graphics.f("#FFFFFF").s().p("AgjApQgKgNAEgVIACgNQADgWANgNQANgNATAAQASAAAKAOQAJANgEAVIgCANQgDAWgOANQgMANgSAAQgUAAgIgOgAgGgYQgEAHgCALIgCANQgCAMACAHQACAHAIAAQAGAAAFgHQADgHACgMIACgNQADgMgDgHQgCgHgHAAQgGAAgFAIg");
        this.shape_405.setTransform(135.3, -139.225);

        this.shape_406 = new cjs.Shape();
        this.shape_406.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_406.setTransform(128.775, -141.575);

        this.shape_407 = new cjs.Shape();
        this.shape_407.graphics.f("#FFFFFF").s().p("AgjApQgKgNAEgVIACgNQADgWANgNQANgNATAAQASAAAKAOQAIANgDAVIgCANQgEAWgNANQgMANgSAAQgUAAgIgOgAgFgYQgFAHgCALIgCANQgCAMACAHQACAHAIAAQAGAAAEgHQAFgHACgMIABgNQACgMgCgHQgCgHgHAAQgGAAgEAIg");
        this.shape_407.setTransform(121.15, -139.225);

        this.shape_408 = new cjs.Shape();
        this.shape_408.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_408.setTransform(114.625, -141.575);

        this.shape_409 = new cjs.Shape();
        this.shape_409.graphics.f("#FFFFFF").s().p("AgJBKQgFgDgDgGIgEAKIgYAAIAfiXIAaAAIgKA1QAEgEAFgCQAEgCAFAAQAQAAAGAQQAHAPgFAZIgBACQgFAZgKANQgKAMgQAAQgGAAgFgDgAADgGQgDADgCAEIgKAxQABADADABQACABAEAAQAHAAAEgHQAEgHADgOIAAgCQAEgRgBgIQgBgJgIAAQgDAAgEADg");
        this.shape_409.setTransform(106.7837, -141.475);

        this.shape_410 = new cjs.Shape();
        this.shape_410.graphics.f("#FFFFFF").s().p("AgTAbIALgeIAFgXIAXAAIgEAYIgTAdg");
        this.shape_410.setTransform(94.45, -133.625);

        this.shape_411 = new cjs.Shape();
        this.shape_411.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_411.setTransform(91.725, -141.575);

        this.shape_412 = new cjs.Shape();
        this.shape_412.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_412.setTransform(84.2393, -139.225);

        this.shape_413 = new cjs.Shape();
        this.shape_413.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_413.setTransform(75.7419, -139.225);

        this.shape_414 = new cjs.Shape();
        this.shape_414.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_414.setTransform(69.525, -141.575);

        this.shape_415 = new cjs.Shape();
        this.shape_415.graphics.f("#FFFFFF").s().p("AAbA2IANg/QACgNgBgEQgCgFgFAAQgDAAgDADQgEACgDAEIAAAEIgBAEIgNBEIgbAAIANg/QACgNgBgEQgBgFgGAAQgDAAgDACIgGAGIgPBNIgcAAIAWhpIAZAAIgBAMQAFgHAHgDQAGgEAHAAQAHAAAFAEQAFAEABAJQAGgIAHgFQAHgEAIAAQAMAAAFALQAFALgEAWIgNA/g");
        this.shape_415.setTransform(59.2889, -139.325);

        this.shape_416 = new cjs.Shape();
        this.shape_416.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_416.setTransform(48.1799, -139.225);

        this.shape_417 = new cjs.Shape();
        this.shape_417.graphics.f("#FFFFFF").s().p("AAEBMIANg+QACgOgBgEQgCgEgGAAQgDAAgEACIgEAEIgQBOIgcAAIAfiXIAaAAIgLA5IAAABQAFgHAGgDQAGgDAHAAQAMAAAGALQAGALgFAWIgMA+g");
        this.shape_417.setTransform(38.6464, -141.575);

        this.shape_418 = new cjs.Shape();
        this.shape_418.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_418.setTransform(30.0919, -139.225);

        this.shape_419 = new cjs.Shape();
        this.shape_419.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_419.setTransform(19.575, -141.575);

        this.shape_420 = new cjs.Shape();
        this.shape_420.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_420.setTransform(14.825, -141.575);

        this.shape_421 = new cjs.Shape();
        this.shape_421.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_421.setTransform(7.3393, -139.225);

        this.shape_422 = new cjs.Shape();
        this.shape_422.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIABAAQADgHAGgEQAFgEAFAAIADABIAEAAIgIAaIgKAAQgDAAgDACQgEACgCAEIgNBIg");
        this.shape_422.setTransform(-4.05, -139.325);

        this.shape_423 = new cjs.Shape();
        this.shape_423.graphics.f("#FFFFFF").s().p("AgkApQgIgNADgVIACgNQAEgWANgNQAMgNASAAQAUAAAIAOQAKANgEAVIgCANQgDAWgOANQgMANgTAAQgSAAgKgOgAgFgYQgFAHgCALIgBANQgDAMACAHQACAHAIAAQAGAAAEgHQAEgHADgMIABgNQACgMgCgHQgBgHgIAAQgGAAgEAIg");
        this.shape_423.setTransform(-12.25, -139.225);

        this.shape_424 = new cjs.Shape();
        this.shape_424.graphics.f("#FFFFFF").s().p("AgmBNIARhUIgMAAIAEgUIAMAAIADgMQADgTALgJQAJgJARAAIAGAAIAHACIgHAVIgDgBIgEAAQgFAAgDAEQgDAEgCAHIgCAMIAQAAIgEAUIgQAAIgQBUg");
        this.shape_424.setTransform(-18.725, -141.675);

        this.shape_425 = new cjs.Shape();
        this.shape_425.graphics.f("#FFFFFF").s().p("AAbA2IANg/QACgNgBgEQgCgFgFAAQgDAAgDADQgEACgDAEIAAAEIgBAEIgNBEIgbAAIANg/QACgNgBgEQgBgFgGAAQgDAAgDACIgGAGIgPBNIgcAAIAWhpIAZAAIgBAMQAFgHAHgDQAGgEAHAAQAHAAAFAEQAFAEABAJQAGgIAHgFQAHgEAIAAQAMAAAFALQAFALgEAWIgNA/g");
        this.shape_425.setTransform(-34.7111, -139.325);

        this.shape_426 = new cjs.Shape();
        this.shape_426.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIABAAQAEgHAFgEQAEgEAGAAIADABIAEAAIgIAaIgKAAQgEAAgDACQgDACgCAEIgNBIg");
        this.shape_426.setTransform(-44.25, -139.325);

        this.shape_427 = new cjs.Shape();
        this.shape_427.graphics.f("#FFFFFF").s().p("AgjApQgKgNAEgVIACgNQADgWANgNQANgNATAAQASAAAKAOQAIANgDAVIgCANQgEAWgNANQgMANgSAAQgUAAgIgOgAgGgYQgEAHgCALIgCANQgCAMACAHQACAHAIAAQAGAAAEgHQAFgHABgMIACgNQACgMgCgHQgCgHgHAAQgGAAgFAIg");
        this.shape_427.setTransform(-52.45, -139.225);

        this.shape_428 = new cjs.Shape();
        this.shape_428.graphics.f("#FFFFFF").s().p("AgmBNIARhUIgMAAIAEgUIAMAAIADgMQADgTALgJQAJgJARAAIAGAAIAHACIgHAVIgDgBIgEAAQgFAAgDAEQgDAEgCAHIgCAMIAQAAIgEAUIgQAAIgQBUg");
        this.shape_428.setTransform(-58.925, -141.675);

        this.shape_429 = new cjs.Shape();
        this.shape_429.graphics.f("#FFFFFF").s().p("AgYA6QgGgHAEgSIAKg0IgLAAIAEgVIALAAIAGgaIAaAAIgFAaIAOAAIgEAVIgOAAIgLA0QAAAGAAADQAAABABAAQAAABABAAQAAAAABAAQAAABABAAIAEAAIAEgBIgCAUIgJACIgHABQgNAAgFgJg");
        this.shape_429.setTransform(-65.6681, -140.45);

        this.shape_430 = new cjs.Shape();
        this.shape_430.graphics.f("#FFFFFF").s().p("AglAuQgGgJACgOQAEgQALgHQALgIASAAIAJAAIACgJQACgIgCgEQgCgEgFAAQgDAAgDADQgCAEgBAGIgbAAIAAgBQACgPAMgJQAMgJAQAAQAPAAAJAKQAIAKgDARIgJAqQgCAIAAAHQAAAGABAHIgcAAIAAgGIgBgGQgDAGgGAEQgGAEgHAAQgMAAgGgJgAgIALQgDAEgCAHQgBAGACADQABADAEAAQADAAAEgCQADgCACgEIAEgUIgJAAQgEAAgEAFg");
        this.shape_430.setTransform(-74.0107, -139.225);

        this.shape_431 = new cjs.Shape();
        this.shape_431.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_431.setTransform(-80.175, -141.575);

        this.shape_432 = new cjs.Shape();
        this.shape_432.graphics.f("#FFFFFF").s().p("AgxBKIAdiRIAZAAIgBAIQAFgFAFgDQAFgCAFAAQAPAAAHARQAHAQgFAYIgBACQgFAYgKANQgKANgQAAQgGAAgEgCQgFgDgDgEIgJAvgAAHgwQgDACgDAFIgJAyQAAABAAAAQABABAAAAQABABAAAAQABAAAAAAIAGABQAHABAFgIQAEgGADgOIAAgCQAEgQgCgKQgBgJgHAAQgDAAgEADg");
        this.shape_432.setTransform(-88.4208, -137.3);

        this.shape_433 = new cjs.Shape();
        this.shape_433.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_433.setTransform(-101.1201, -139.225);

        this.shape_434 = new cjs.Shape();
        this.shape_434.graphics.f("#FFFFFF").s().p("AgjA2IAVhpIAZAAIgCANIAAAAQAEgHAFgEQAFgEAGAAIAEABIADAAIgIAaIgKAAQgDAAgEACQgDACgCAEIgOBIg");
        this.shape_434.setTransform(-108.4, -139.325);

        this.shape_435 = new cjs.Shape();
        this.shape_435.graphics.f("#FFFFFF").s().p("AgqArQgGgLAFgWIANg/IAbAAIgNA/QgCAMABAFQACAFAFAAQAEAAADgCQADgCADgDIAPhOIAcAAIgVBpIgYAAIAAgLQgEAGgGADQgHAEgHAAQgNAAgGgLg");
        this.shape_435.setTransform(-116.2464, -139.125);

        this.shape_436 = new cjs.Shape();
        this.shape_436.graphics.f("#FFFFFF").s().p("AghApQgJgNADgVIADgNQADgWANgNQAMgNATAAQAQAAAIALQAIALgDATIgZAAQABgJgCgFQgBgFgGAAQgHAAgDAHQgEAIgCALIgCANQgCAMACAHQABAHAIAAQAEAAADgFQAEgEABgIIAZAAIAAABQgDARgLAKQgMALgOAAQgTAAgJgOg");
        this.shape_436.setTransform(-125.4581, -139.225);

        this.shape_437 = new cjs.Shape();
        this.shape_437.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_437.setTransform(-134.1701, -139.225);

        this.shape_438 = new cjs.Shape();
        this.shape_438.graphics.f("#FFFFFF").s().p("AghAsQgJgLACgNIABAAIAZAAQgBAIADAEQACADAGAAQAEAAADgCQADgDABgFQABgFgDgDIgKgIQgOgGgHgGQgGgIACgMQACgNALgJQALgJAPAAQARAAAJAKQAIAJgCAOIgBABIgaAAQABgHgCgEQgCgDgFAAQgDAAgDADQgDADgBAEQgBAFADADIAKAHQAPAGAGAHQAHAIgCAMQgDAOgLAJQgLAIgQAAQgRAAgJgLg");
        this.shape_438.setTransform(-142.9156, -139.225);

        this.shape_439 = new cjs.Shape();
        this.shape_439.graphics.f("#FFFFFF").s().p("AgUAbIAMgeIAFgXIAYAAIgFAYIgTAdg");
        this.shape_439.setTransform(-154.9, -133.625);

        this.shape_440 = new cjs.Shape();
        this.shape_440.graphics.f("#FFFFFF").s().p("AggAqQgKgOADgTIADgOQADgWANgOQAMgNASAAQARAAAIANQAIAMgDAVIgDAQIgwAAIAAABQgCAKADAHQADAHAIAAQAHAAAGgCIAMgGIAEASQgGAFgKADQgJAEgJAAQgTAAgJgNgAgCgaQgEAHgCAKIAVAAIABgEQABgJgCgFQgBgFgGAAQgFAAgDAGg");
        this.shape_440.setTransform(-160.1201, -139.225);

        this.shape_441 = new cjs.Shape();
        this.shape_441.graphics.f("#FFFFFF").s().p("AgcBMIAdiXIAcAAIgeCXg");
        this.shape_441.setTransform(-166.475, -141.575);

        this.shape_442 = new cjs.Shape();
        this.shape_442.graphics.f("#FFFFFF").s().p("AgkBJQgHgDgGgDIAJgWQAFAEAFACQAFABAHAAQAHAAAGgHQAFgFACgLIAAgDQgDAFgFACQgFACgFAAQgPAAgGgQQgIgPAFgWIABgCQAFgbAKgOQALgNAPAAQAHAAAFADQAFADADAGIAEgKIAYAAIgVBlQgEAWgOAMQgNAMgVAAQgGAAgHgCgAgEgsQgFAJgDAPIAAACQgDAOABAIQABAJAHgBQAEAAACgCQAEgCADgFIAKgxQgBgDgDgCQgDgBgDAAQgHAAgEAIg");
        this.shape_442.setTransform(-174.125, -137.2);

        this.shape_443 = new cjs.Shape();
        this.shape_443.graphics.f("#FFFFFF").s().p("AADA2IANhBQADgLgCgFQgCgEgGAAQgDAAgDACIgFAGIgQBNIgbAAIAVhpIAZAAIgBAOQAFgIAGgEQAHgEAIAAQALAAAGAKQAFALgEAVIgNBBg");
        this.shape_443.setTransform(-183.7408, -139.325);

        this.shape_444 = new cjs.Shape();
        this.shape_444.graphics.f("#FFFFFF").s().p("AgcBMIAVhoIAbAAIgVBogAgCg2IADgVIAcAAIgEAVg");
        this.shape_444.setTransform(-190.025, -141.575);

        this.shape_445 = new cjs.Shape();
        this.shape_445.graphics.f("#FFFFFF").s().p("AghAsQgJgLACgNIABAAIAZAAQgBAIADAEQACADAGAAQAEAAADgCQADgDABgFQABgFgDgDIgKgIQgOgGgHgGQgGgIACgMQACgNALgJQALgJAPAAQARAAAJAKQAIAJgCAOIgBABIgaAAQABgHgCgEQgCgDgFAAQgDAAgDADQgDADgBAEQgBAFADADIAKAHQAPAGAGAHQAHAIgCAMQgDAOgLAJQgLAIgQAAQgRAAgJgLg");
        this.shape_445.setTransform(-197.1656, -139.225);

        this.shape_446 = new cjs.Shape();
        this.shape_446.graphics.f("#FFFFFF").s().p("AAYBHIgBgeIghAAIgNAeIgcAAIBBiNIAdAAIAJCNgAAAASIAWAAIgCgvIAAAAg");
        this.shape_446.setTransform(-211.875, -141.05);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_446 }, { t: this.shape_445 }, { t: this.shape_444 }, { t: this.shape_443 }, { t: this.shape_442 }, { t: this.shape_441 }, { t: this.shape_440 }, { t: this.shape_439 }, { t: this.shape_438 }, { t: this.shape_437 }, { t: this.shape_436 }, { t: this.shape_435 }, { t: this.shape_434 }, { t: this.shape_433 }, { t: this.shape_432 }, { t: this.shape_431 }, { t: this.shape_430 }, { t: this.shape_429 }, { t: this.shape_428 }, { t: this.shape_427 }, { t: this.shape_426 }, { t: this.shape_425 }, { t: this.shape_424 }, { t: this.shape_423 }, { t: this.shape_422 }, { t: this.shape_421 }, { t: this.shape_420 }, { t: this.shape_419 }, { t: this.shape_418 }, { t: this.shape_417 }, { t: this.shape_416 }, { t: this.shape_415 }, { t: this.shape_414 }, { t: this.shape_413 }, { t: this.shape_412 }, { t: this.shape_411 }, { t: this.shape_410 }, { t: this.shape_409 }, { t: this.shape_408 }, { t: this.shape_407 }, { t: this.shape_406 }, { t: this.shape_405 }, { t: this.shape_404 }, { t: this.shape_403 }, { t: this.shape_402 }, { t: this.shape_401 }, { t: this.shape_400 }, { t: this.shape_399 }, { t: this.shape_398 }, { t: this.shape_397 }, { t: this.shape_396 }, { t: this.shape_395 }, { t: this.shape_394 }, { t: this.shape_393 }, { t: this.shape_392 }, { t: this.shape_391 }, { t: this.shape_390 }, { t: this.shape_389 }, { t: this.shape_388 }, { t: this.shape_387 }, { t: this.shape_386 }, { t: this.shape_385 }, { t: this.shape_384 }, { t: this.shape_383 }, { t: this.shape_382 }, { t: this.shape_381 }, { t: this.shape_380 }, { t: this.shape_379 }, { t: this.shape_378 }, { t: this.shape_377 }, { t: this.shape_376 }, { t: this.shape_375 }, { t: this.shape_374 }, { t: this.shape_373 }, { t: this.shape_372 }, { t: this.shape_371 }, { t: this.shape_370 }, { t: this.shape_369 }, { t: this.shape_368 }, { t: this.shape_367 }, { t: this.shape_366 }, { t: this.shape_365 }, { t: this.shape_364 }, { t: this.shape_363 }, { t: this.shape_362 }, { t: this.shape_361 }, { t: this.shape_360 }, { t: this.shape_359 }, { t: this.shape_358 }, { t: this.shape_357 }, { t: this.shape_356 }, { t: this.shape_355 }, { t: this.shape_354 }, { t: this.shape_353 }, { t: this.shape_352 }, { t: this.shape_351 }, { t: this.shape_350 }, { t: this.shape_349 }, { t: this.shape_348 }, { t: this.shape_347 }, { t: this.shape_346 }, { t: this.shape_345 }, { t: this.shape_344 }, { t: this.shape_343 }, { t: this.shape_342 }, { t: this.shape_341 }, { t: this.shape_340 }, { t: this.shape_339 }, { t: this.shape_338 }, { t: this.shape_337 }, { t: this.shape_336 }, { t: this.shape_335 }, { t: this.shape_334 }, { t: this.shape_333 }, { t: this.shape_332 }, { t: this.shape_331 }, { t: this.shape_330 }, { t: this.shape_329 }, { t: this.shape_328 }, { t: this.shape_327 }, { t: this.shape_326 }, { t: this.shape_325 }, { t: this.shape_324 }, { t: this.shape_323 }, { t: this.shape_322 }, { t: this.shape_321 }, { t: this.shape_320 }, { t: this.shape_319 }, { t: this.shape_318 }, { t: this.shape_317 }, { t: this.shape_316 }, { t: this.shape_315 }, { t: this.shape_314 }, { t: this.shape_313 }, { t: this.shape_312 }, { t: this.shape_311 }, { t: this.shape_310 }, { t: this.shape_309 }, { t: this.shape_308 }, { t: this.shape_307 }, { t: this.shape_306 }, { t: this.shape_305 }, { t: this.shape_304 }, { t: this.shape_303 }, { t: this.shape_302 }, { t: this.shape_301 }, { t: this.shape_300 }, { t: this.shape_299 }, { t: this.shape_298 }, { t: this.shape_297 }, { t: this.shape_296 }, { t: this.shape_295 }, { t: this.shape_294 }, { t: this.shape_293 }, { t: this.shape_292 }, { t: this.shape_291 }, { t: this.shape_290 }, { t: this.shape_289 }, { t: this.shape_288 }, { t: this.shape_287 }, { t: this.shape_286 }, { t: this.shape_285 }, { t: this.shape_284 }, { t: this.shape_283 }, { t: this.shape_282 }, { t: this.shape_281 }, { t: this.shape_280 }, { t: this.shape_279 }, { t: this.shape_278 }, { t: this.shape_277 }, { t: this.shape_276 }, { t: this.shape_275 }, { t: this.shape_274 }, { t: this.shape_273 }, { t: this.shape_272 }, { t: this.shape_271 }, { t: this.shape_270 }, { t: this.shape_269 }, { t: this.shape_268 }, { t: this.shape_267 }, { t: this.shape_266 }, { t: this.shape_265 }, { t: this.shape_264 }, { t: this.shape_263 }, { t: this.shape_262 }, { t: this.shape_261 }, { t: this.shape_260 }, { t: this.shape_259 }, { t: this.shape_258 }, { t: this.shape_257 }, { t: this.shape_256 }, { t: this.shape_255 }, { t: this.shape_254 }, { t: this.shape_253 }, { t: this.shape_252 }, { t: this.shape_251 }, { t: this.shape_250 }, { t: this.shape_249 }, { t: this.shape_248 }, { t: this.shape_247 }, { t: this.shape_246 }, { t: this.shape_245 }, { t: this.shape_244 }, { t: this.shape_243 }, { t: this.shape_242 }, { t: this.shape_241 }, { t: this.shape_240 }, { t: this.shape_239 }, { t: this.shape_238 }, { t: this.shape_237 }, { t: this.shape_236 }, { t: this.shape_235 }, { t: this.shape_234 }, { t: this.shape_233 }, { t: this.shape_232 }, { t: this.shape_231 }, { t: this.shape_230 }, { t: this.shape_229 }, { t: this.shape_228 }, { t: this.shape_227 }, { t: this.shape_226 }, { t: this.shape_225 }, { t: this.shape_224 }, { t: this.shape_223 }, { t: this.shape_222 }, { t: this.shape_221 }, { t: this.shape_220 }, { t: this.shape_219 }, { t: this.shape_218 }, { t: this.shape_217 }, { t: this.shape_216 }, { t: this.shape_215 }, { t: this.shape_214 }, { t: this.shape_213 }, { t: this.shape_212 }, { t: this.shape_211 }, { t: this.shape_210 }, { t: this.shape_209 }, { t: this.shape_208 }, { t: this.shape_207 }, { t: this.shape_206 }, { t: this.shape_205 }, { t: this.shape_204 }, { t: this.shape_203 }, { t: this.shape_202 }, { t: this.shape_201 }, { t: this.shape_200 }, { t: this.shape_199 }, { t: this.shape_198 }, { t: this.shape_197 }, { t: this.shape_196 }, { t: this.shape_195 }, { t: this.shape_194 }, { t: this.shape_193 }, { t: this.shape_192 }, { t: this.shape_191 }, { t: this.shape_190 }, { t: this.shape_189 }, { t: this.shape_188 }, { t: this.shape_187 }, { t: this.shape_186 }, { t: this.shape_185 }, { t: this.shape_184 }, { t: this.shape_183 }, { t: this.shape_182 }, { t: this.shape_181 }, { t: this.shape_180 }, { t: this.shape_179 }, { t: this.shape_178 }, { t: this.shape_177 }, { t: this.shape_176 }, { t: this.shape_175 }, { t: this.shape_174 }, { t: this.shape_173 }, { t: this.shape_172 }, { t: this.shape_171 }, { t: this.shape_170 }, { t: this.shape_169 }, { t: this.shape_168 }, { t: this.shape_167 }, { t: this.shape_166 }, { t: this.shape_165 }, { t: this.shape_164 }, { t: this.shape_163 }, { t: this.shape_162 }, { t: this.shape_161 }, { t: this.shape_160 }, { t: this.shape_159 }, { t: this.shape_158 }, { t: this.shape_157 }, { t: this.shape_156 }, { t: this.shape_155 }, { t: this.shape_154 }, { t: this.shape_153 }, { t: this.shape_152 }, { t: this.shape_151 }, { t: this.shape_150 }, { t: this.shape_149 }, { t: this.shape_148 }, { t: this.shape_147 }, { t: this.shape_146 }, { t: this.shape_145 }, { t: this.shape_144 }, { t: this.shape_143 }, { t: this.shape_142 }, { t: this.shape_141 }, { t: this.shape_140 }, { t: this.shape_139 }, { t: this.shape_138 }, { t: this.shape_137 }, { t: this.shape_136 }, { t: this.shape_135 }, { t: this.shape_134 }, { t: this.shape_133 }, { t: this.shape_132 }, { t: this.shape_131 }, { t: this.shape_130 }, { t: this.shape_129 }, { t: this.shape_128 }, { t: this.shape_127 }, { t: this.shape_126 }, { t: this.shape_125 }, { t: this.shape_124 }, { t: this.shape_123 }, { t: this.shape_122 }, { t: this.shape_121 }, { t: this.shape_120 }, { t: this.shape_119 }, { t: this.shape_118 }, { t: this.shape_117 }, { t: this.shape_116 }, { t: this.shape_115 }, { t: this.shape_114 }, { t: this.shape_113 }, { t: this.shape_112 }, { t: this.shape_111 }, { t: this.shape_110 }, { t: this.shape_109 }, { t: this.shape_108 }, { t: this.shape_107 }, { t: this.shape_106 }, { t: this.shape_105 }, { t: this.shape_104 }, { t: this.shape_103 }, { t: this.shape_102 }, { t: this.shape_101 }, { t: this.shape_100 }, { t: this.shape_99 }, { t: this.shape_98 }, { t: this.shape_97 }, { t: this.shape_96 }, { t: this.shape_95 }, { t: this.shape_94 }, { t: this.shape_93 }, { t: this.shape_92 }, { t: this.shape_91 }, { t: this.shape_90 }, { t: this.shape_89 }, { t: this.shape_88 }, { t: this.shape_87 }, { t: this.shape_86 }, { t: this.shape_85 }, { t: this.shape_84 }, { t: this.shape_83 }, { t: this.shape_82 }, { t: this.shape_81 }, { t: this.shape_80 }, { t: this.shape_79 }, { t: this.shape_78 }, { t: this.shape_77 }, { t: this.shape_76 }, { t: this.shape_75 }, { t: this.shape_74 }, { t: this.shape_73 }, { t: this.shape_72 }, { t: this.shape_71 }, { t: this.shape_70 }, { t: this.shape_69 }, { t: this.shape_68 }, { t: this.shape_67 }, { t: this.shape_66 }, { t: this.shape_65 }, { t: this.shape_64 }, { t: this.shape_63 }, { t: this.shape_62 }, { t: this.shape_61 }, { t: this.shape_60 }, { t: this.shape_59 }, { t: this.shape_58 }, { t: this.shape_57 }, { t: this.shape_56 }, { t: this.shape_55 }, { t: this.shape_54 }, { t: this.shape_53 }, { t: this.shape_52 }, { t: this.shape_51 }, { t: this.shape_50 }, { t: this.shape_49 }, { t: this.shape_48 }, { t: this.shape_47 }, { t: this.shape_46 }, { t: this.shape_45 }, { t: this.shape_44 }, { t: this.shape_43 }, { t: this.shape_42 }, { t: this.shape_41 }, { t: this.shape_40 }, { t: this.shape_39 }, { t: this.shape_38 }, { t: this.shape_37 }, { t: this.shape_36 }, { t: this.shape_35 }, { t: this.shape_34 }, { t: this.shape_33 }, { t: this.shape_32 }, { t: this.shape_31 }, { t: this.shape_30 }, { t: this.shape_29 }, { t: this.shape_28 }, { t: this.shape_27 }, { t: this.shape_26 }, { t: this.shape_25 }, { t: this.shape_24 }, { t: this.shape_23 }, { t: this.shape_22 }, { t: this.shape_21 }, { t: this.shape_20 }, { t: this.shape_19 }, { t: this.shape_18 }, { t: this.shape_17 }, { t: this.shape_16 }, { t: this.shape_15 }, { t: this.shape_14 }, { t: this.shape_13 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-218.4, -156.9, 436.9, 313.9);


    (lib.cbrn_tab_button = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#990000").s().p("AbiNSMhFXAAAIAA6kMBFpAAAIAOgBIAgAAQFhAAD5D5QD6D6AAFgQAAFhj6D5QjjDkk5AUQgfACgfAAQghAAgfgCg");
        this.shape.setTransform(-0.025, 0.025);
        this.shape._off = true;

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(3).to({ _off: false }, 0).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-267.8, -85.2, 535.6, 170.5);


    (lib.cbrn_blue = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#3770A7").s().p("EhQBAieMAAAhE7MCgDAAAMAAABE7g");
        this.shape.setTransform(0, 0.025);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cbrn_blue, new cjs.Rectangle(-512.1, -220.5, 1024.3000000000002, 441.1), null);


    (lib.br_tint = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("rgba(172,43,58,0.498)").s().p("EhP/A8FMAAAh4JMCf/AAAMAAAB4Jg");
        this.shape.setTransform(512, 384.525);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.br_tint, new cjs.Rectangle(0, 0, 1024, 769.1), null);


    (lib.ClipGroup_2_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        mask_1.graphics.p("ALgOIQBGjLhejCQhfjCjLhGQiqg7iqA7QipA7hhCYIhDgnQAVg6gag4Qgag4g6gVQgOgFgMgCIAAhNQDYgKCQieQCQiggKjXQgIi0h1iIQh2iIiwgiIAAgcQCVAUCBBMQB8BKBYBzQBYB0AmCMQAoCQgUCWIgDAXQCNA1BtBmQBpBjA8CFQA8CEAGCRQAGCWg1CNIAAABIgIAVgAmOiDQitAAiIBrQhCg5gqhOQC3iRDqAAQDpAAC2CRQgpBNhCA6QiIhrisAAg");
        mask_1.setTransform(270.7305, 150.325);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#DFDFDF").s().p("Ax1YvIgCgBIACgBIJiBLIHzupQAAAAABAAQAAgBAAAAQAAAAAAAAQAAgBABABMASRgiOIoFi2IgBgBQgBAAAAAAQAAAAAAAAQAAgBAAAAQAAAAABAAQAAAAAAgBQAAAAABAAQAAAAAAAAQABAAAAAAIIIC1IABABIAAACMgSSAiQIgBABIAAAAIn2OpQAAABAAAAQAAAAAAAAQgBAAAAAAQAAAAAAAAg");
        this.shape.setTransform(324.85, 255.2);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#DFDFDF").s().p("AHCRlIksiWIgFgDIgBACIgMgIIkkiSIgMgHIkXiLQgHgCgHgDIgNgIIk3ibIgLgEIomkSQgagNgKgcQgJgdANgaQANgbAcgJQAdgJAaANIImERIAAABIEPCFICKkVIgCgDIjHltQgOgbAIgcQAIgcAagPIABAAQAagOAcAIQAdAJAOAaICZEYIB1jpIACgEIA6h0IFssrQANgaAbgKQAcgKAbANQAaANALAbQAKAcgMAbImLNmICxBYIGJtjQAMgaAcgLQAbgKAcAMQAaAMALAcQAKAcgMAbIluMrIgdA7ICxBWIGnucQAMgaAbgLQAbgLAbALQAaANALAbQAKAcgMAbImKNlIgCADIgcA6ICzBYIG3u+QANgbAdgKQAcgJAaANQAbANAKAcQAJAdgNAaImcOHImpNXQgOAbgbAJQgMAEgLAAQgQAAgQgIgAG4IdIjJE/IDTBpIEtpeIiwhYgAggLUIBiAxIDFjuIiihQgAlQI7ICwBYICFkLIiwhYgADCENICcCDIBfjBIixhYgAiMCvICwBZIBojRIiwhXg");
        this.shape_1.setTransform(139.325, 139.5411);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#F8CB5F").s().p("AhYDwQhXgShVggQgCAAgEgDIjXhqQgbgNgJgdQgKgcAOgaQANgaAcgKQAcgJAbANIDXBoIAAABQBMAdBUAQQBAAOBBgHQAhgCAhgMQAlgNAdgcIABAAQBPhQAyhkQAVgvAMgjIABgBQAKgbAagNQAbgMAbAKQAcAKANAbQANAbgJAcQgRAzgYAvQg9B5hhBhIgDABQgoAkgqAVQgwAXgyAJQgpAHguAAQg/AAhKgPg");
        this.shape_2.setTransform(79.9129, 236.5701);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#F8CB5F").s().p("AYjIFIhPgFIgGAAI20hkQgMgBgKgCImzgmQg2ALg0ABQhGADhFgJQiigWigg0QhkgfiXg8Qi8hNjFhiIAAAAQgvgXgQgvQgRgxAXguQAXgvAxgRQAxgQAvAXQDnB0DuBZQDOBNCTAYQBTAQBQgDQAYgCAZgFQAPgGAQgCIAYgJQAOgGANgLQBzhiBdh6QAigtALh/QAMiKAZgxQAEgIA3ATQA3ATAEgJQAUgsArgGQAlgGAzAYQA8AcAKAKQAVAVgUAqQgnBdgQAeIhPCcQgpBNgwA/QgxBBg3A7ICVAOIATADIWnBiIABAAIBMAFIABAAQA0AEAiAnQAiAogEAzQgEA0gnAiQgkAegtAAIgKAAg");
        this.shape_3.setTransform(171.6848, 288.4439);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#9E000B").s().p("AnnCLIDGmNIExCZICnlRIExCYIltLdg");
        this.shape_4.setTransform(144.7, 186.125);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FF0000").s().p("A4JSxICKkTIAAAAIDUmrIh0rPIK2m4IgBgBIJfzGIYVJvInIR4QhSGrjiFrQjoF1lZDvIl5JWgApKBzIJiEwIFtrcIkwiZIgBABIAAAAIimFQIkyiYg");
        this.shape_5.setTransform(154.6, 188.375);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#3770A7").s().p("AsGa1IrdltIF6pWQJzpNECssIHGx5IUSHIMgaIAw5g");
        this.shape_6.setTransform(288.25, 241.525);

        var maskedShapeInstanceList = [this.shape, this.shape_1, this.shape_2, this.shape_3, this.shape_4, this.shape_5, this.shape_6];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_2_1, new cjs.Rectangle(189.2, 58.5, 163.10000000000002, 183.7), null);


    (lib.ClipGroup_1_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        mask_1.graphics.p("AEaIVQiQgmh4hcIgSgPQh0BgiQArQiKApiRgPQiRgOiAhDQiFhFhgh1IgOgRIAYgOQCNCjDXAPQDXAPCiiNQCIh2AjixQAiiwhTigIBCgmQAoAvA8AGQA9AGAvgnQALgJAKgLIBCAmQhjC/BBDNQBBDNDABjQCgBTCxgiQCxgiB2iIIAYAOQhdB3iCBKQh+BGiQATQgwAGgwAAQhfAAhegYgAC2ADQChg/BXiVQBWiWgairQBUgdBXADQAjDnh1DLQh0DJjaBVQgthLgShWg");
        mask_1.setTransform(230.8, 232.4441);

        // Layer_3
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#DFDFDF").s().p("Ax1YvIgCgBIACgBIJiBLIHzupQAAAAABAAQAAgBAAAAQAAAAAAAAQAAgBABABMASRgiOIoFi2IgBgBQgBAAAAAAQAAAAAAAAQAAgBAAAAQAAAAABAAQAAAAAAgBQAAAAABAAQAAAAAAAAQABAAAAAAIIIC1IABABIAAACMgSSAiQIgBABIAAAAIn2OpQAAABAAAAQAAAAAAAAQgBAAAAAAQAAAAAAAAg");
        this.shape.setTransform(324.85, 255.2);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#DFDFDF").s().p("AHCRlIksiWIgFgDIgBACIgMgIIkkiSIgMgHIkXiLQgHgCgHgDIgNgIIk3ibIgLgEIomkSQgagNgKgcQgJgdANgaQANgbAcgJQAdgJAaANIImERIAAABIEPCFICKkVIgCgDIjHltQgOgbAIgcQAIgcAagPIABAAQAagOAcAIQAdAJAOAaICZEYIB1jpIACgEIA6h0IFssrQANgaAbgKQAcgKAbANQAaANALAbQAKAcgMAbImLNmICxBYIGJtjQAMgaAcgLQAbgKAcAMQAaAMALAcQAKAcgMAbIluMrIgdA7ICxBWIGnucQAMgaAbgLQAbgLAbALQAaANALAbQAKAcgMAbImKNlIgCADIgcA6ICzBYIG3u+QANgbAdgKQAcgJAaANQAbANAKAcQAJAdgNAaImcOHImpNXQgOAbgbAJQgMAEgLAAQgQAAgQgIgAG4IdIjJE/IDTBpIEtpeIiwhYgAggLUIBiAxIDFjuIiihQgAlQI7ICwBYICFkLIiwhYgADCENICcCDIBfjBIixhYgAiMCvICwBZIBojRIiwhXg");
        this.shape_1.setTransform(139.325, 139.5411);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#9E000B").s().p("AhYDwQhXgShVggQgCAAgEgDIjXhqQgbgNgJgdQgKgcAOgaQANgaAcgKQAcgJAbANIDXBoIAAABQBMAdBUAQQBAAOBBgHQAhgCAhgMQAlgNAdgcIABAAQBPhQAyhkQAVgvAMgjIABgBQAKgbAagNQAbgMAbAKQAcAKANAbQANAbgJAcQgRAzgYAvQg9B5hhBhIgDABQgoAkgqAVQgwAXgyAJQgpAHguAAQg/AAhKgPg");
        this.shape_2.setTransform(79.9129, 236.5701);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#9E000B").s().p("AYjIFIhPgFIgGAAI20hkQgMgBgKgCImzgmQg2ALg0ABQhGADhFgJQiigWigg0QhkgfiXg8Qi8hNjFhiIAAAAQgvgXgQgvQgRgxAXguQAXgvAxgRQAxgQAvAXQDnB0DuBZQDOBNCTAYQBTAQBQgDQAYgCAZgFQAPgGAQgCIAYgJQAOgGANgLQBzhiBdh6QAigtALh/QAMiKAZgxQAEgIA3ATQA3ATAEgJQAUgsArgGQAlgGAzAYQA8AcAKAKQAVAVgUAqQgnBdgQAeIhPCcQgpBNgwA/QgxBBg3A7ICVAOIATADIWnBiIABAAIBMAFIABAAQA0AEAiAnQAiAogEAzQgEA0gnAiQgkAegtAAIgKAAg");
        this.shape_3.setTransform(171.6848, 288.4439);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#9E000B").s().p("AnnCLIDGmNIExCZICnlRIExCYIltLdg");
        this.shape_4.setTransform(144.7, 186.125);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FF0000").s().p("A4JSxICKkTIAAAAIDUmrIh0rPIK2m4IgBgBIJfzGIYVJvInIR4QhSGrjiFrQjoF1lZDvIl5JWgApKBzIJiEwIFtrcIkwiZIgBABIAAAAIimFQIkyiYg");
        this.shape_5.setTransform(154.6, 188.375);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#3770A7").s().p("AsGa1IrdltIF6pWQJzpNECssIHGx5IUSHIMgaIAw5g");
        this.shape_6.setTransform(288.25, 241.525);

        var maskedShapeInstanceList = [this.shape, this.shape_1, this.shape_2, this.shape_3, this.shape_4, this.shape_5, this.shape_6];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_1_1, new cjs.Rectangle(124.8, 176.7, 212, 111.5), null);


    (lib.ClipGroup_14 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask_4 = new cjs.Shape();
        mask_4._off = true;
        mask_4.graphics.p("AApKYQh0jKAjjnQBWgDBUAcIAAABQgZCrBWCWQBXCVChBAQgRBWgvBLQjZhWh1jKgApSJTQACiRA3iGQA4iHBmhmQBqhqCLg5IABgBIAVgIQgYiVAjiSQAhiMBUh3QBVh2B6hNQB+hQCVgZIAXgEIAAAcQjTAph5CyQh5CzApDTQAiCxCIB2QCIB1C0AIIAABNQg8AKgkAyQgkAzAKA8QADAPAFANIhDAmQh0i1jSguQjSgui1B0QiYBhg7CqQg7CqA7CrIgYANQg5iMABiVg");
        mask_4.setTransform(168.7981, 155.675);

        // Layer_3
        this.shape_30 = new cjs.Shape();
        this.shape_30.graphics.f("#DFDFDF").s().p("AgYAgQgKgOAAgSQAAgRAKgOQALgNANAAQAPAAAKANQAKAOAAARQAAATgKANQgKANgPAAQgNAAgLgNg");
        this.shape_30.setTransform(165.75, 193.15);

        this.shape_31 = new cjs.Shape();
        this.shape_31.graphics.f("#DFDFDF").s().p("Ax1YvIgCgBIACgBIJiBLIHzupQAAAAABAAQAAgBAAAAQAAAAAAAAQAAgBABABMASRgiOIoFi2IgBgBQgBAAAAAAQAAAAAAAAQAAgBAAAAQAAAAABAAQAAAAAAgBQAAAAABAAQAAAAAAAAQABAAAAAAIIIC1IABABIAAACMgSSAiQIgBABIAAAAIn2OpQAAABAAAAQAAAAAAAAQgBAAAAAAQAAAAAAAAg");
        this.shape_31.setTransform(324.85, 255.2);

        this.shape_32 = new cjs.Shape();
        this.shape_32.graphics.f("#DFDFDF").s().p("AHCRlIksiWIgFgDIgBACIgMgIIkkiSIgMgHIkXiLQgHgCgHgDIgNgIIk3ibIgLgEIomkSQgagNgKgcQgJgdANgaQANgbAcgJQAdgJAaANIImERIAAABIEPCFICKkVIgCgDIjHltQgOgbAIgcQAIgcAagPIABAAQAagOAcAIQAdAJAOAaICZEYIB1jpIACgEIA6h0IFssrQANgaAbgKQAcgKAbANQAaANALAbQAKAcgMAbImLNmICxBYIGJtjQAMgaAcgLQAbgKAcAMQAaAMALAcQAKAcgMAbIluMrIgdA7ICxBWIGnucQAMgaAbgLQAbgLAbALQAaANALAbQAKAcgMAbImKNlIgCADIgcA6ICzBYIG3u+QANgbAdgKQAcgJAaANQAbANAKAcQAJAdgNAaImcOHImpNXQgOAbgbAJQgMAEgLAAQgQAAgQgIgAG4IdIjJE/IDTBpIEtpeIiwhYgAggLUIBiAxIDFjuIiihQgAlQI7ICwBYICFkLIiwhYgADCENICcCDIBfjBIixhYgAiMCvICwBZIBojRIiwhXg");
        this.shape_32.setTransform(139.325, 139.5411);

        this.shape_33 = new cjs.Shape();
        this.shape_33.graphics.f("#9E000B").s().p("AhYDwQhXgShVggQgCAAgEgDIjXhqQgbgNgJgdQgKgcAOgaQANgaAcgKQAcgJAbANIDXBoIAAABQBMAdBUAQQBAAOBBgHQAhgCAhgMQAlgNAdgcIABAAQBPhQAyhkQAVgvAMgjIABgBQAKgbAagNQAbgMAbAKQAcAKANAbQANAbgJAcQgRAzgYAvQg9B5hhBhIgDABQgoAkgqAVQgwAXgyAJQgpAHguAAQg/AAhKgPg");
        this.shape_33.setTransform(79.9129, 236.5701);

        this.shape_34 = new cjs.Shape();
        this.shape_34.graphics.f("#666666").s().p("AYjIFIhPgFIgGAAI20hkQgMgBgKgCImzgmQg2ALg0ABQhGADhFgJQiigWigg0QhkgfiXg8Qi5hMjIhiIAAgBQgvgXgQgvQgRgxAXguQAXgvAxgRQAxgQAvAXQDnB0DuBZQDOBNCTAYQBTAQBQgDQAYgCAZgFQAPgGAQgCIAYgJQAOgGANgLQByhgBeh8QAigtALh/QAMiKAZgxQAEgIA3ATQA3ATAEgJQAUgsArgGQAlgGAzAYQA8AcAKAKQAVAVgUAqQgnBdgQAeIhPCcQgpBNgwA/QgxBBg3A7ICVAOIATADIWnBiIABAAIBMAFIABAAQA0AEAiAnQAiAogEAzQgEA0gnAiQgkAegtAAIgKAAg");
        this.shape_34.setTransform(171.6848, 288.4439);

        this.shape_35 = new cjs.Shape();
        this.shape_35.graphics.f("#9E000B").s().p("AnnCLIDGmNIExCZICnlRIExCYIltLdg");
        this.shape_35.setTransform(144.7, 186.125);

        this.shape_36 = new cjs.Shape();
        this.shape_36.graphics.f("#FF0000").s().p("A4JSxICKkTIAAAAIDUmrIh0rPIK2m4IgBgBIJfzGIYVJvInIR4QhSGrjiFrQjoF1lZDvIl5JWgApKBzIJiEwIFtrcIkwiZIgBABIAAAAIimFQIkyiYg");
        this.shape_36.setTransform(154.6, 188.375);

        this.shape_37 = new cjs.Shape();
        this.shape_37.graphics.f("#3770A7").s().p("AsGa1IrdltIF6pWQJzpNECssIHGx5IUSHIMgaIAw5g");
        this.shape_37.setTransform(288.25, 241.525);

        var maskedShapeInstanceList = [this.shape_30, this.shape_31, this.shape_32, this.shape_33, this.shape_34, this.shape_35, this.shape_36, this.shape_37];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_4;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_37 }, { t: this.shape_36 }, { t: this.shape_35 }, { t: this.shape_34 }, { t: this.shape_33 }, { t: this.shape_32 }, { t: this.shape_31 }, { t: this.shape_30 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_14, new cjs.Rectangle(109.3, 60.5, 119.00000000000001, 190.4), null);


    (lib.pin_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#DFDFDF").s().p("AABFlQhGABhCgbQhCgbgyg0Qg0gygbhBQgbhDABhGQgBhCAYg/QAYg9AtgxIANgNQAygzBBgbQBCgbBGABQBHgBBCAbQBCAbAyAzQAEAGAIAHQAuAxAXA9QAYA/gBBCQABBGgbBDQgbBBgzAyQgxA0hCAbQg/AahFAAIgFAAg");
        this.shape_2.setTransform(0.0027, 45.55);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FF0000").s().p("AmpNyQiwixAAj5QgBh8AxhzIAFgOIIlztIIpT7QAyBzgCB8QAAD6iwCwQixCwj5AAIgDABQj3gBiviwgAiHB8QhCAbgyAzIgNANQguAxgXA+QgYA+ABBCQgBBIAbBCQAcBCAzAxQAyA0BCAbQBCAcBGgCQBHABBCgbQBCgbAyg0QAzgyAbhCQAbhBgBhIQABhCgYg+QgYg+gtgxIgNgNQgygyhBgcQhCgbhHABIgEAAQhFAAg/Aag");
        this.shape_3.setTransform(0.0056, 0.0006);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_3 }, { t: this.shape_2 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.pin_1, new cjs.Rectangle(-60.2, -105.8, 120.4, 211.7), null);


    (lib.blue = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#003461").s().p("EhP/A8DMAAAh4FMCf/AAAMAAAB4Fg");
        this.shape.setTransform(0.025, 0.025);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.blue, new cjs.Rectangle(-511.9, -384.3, 1023.9, 768.7), null);


    (lib.rrText = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.text = new cjs.Text("The “National Standard and Whole Community solution for the collection, management and sharing of radiological data.”  ", "italic bold 20px 'Roboto Condensed'", "#FFFFFF");
        this.text.lineHeight = 28;
        this.text.lineWidth = 242;
        this.text.parent = this;
        this.text.setTransform(-121, -173.4);

        this.text_1 = new cjs.Text("Incorporates federal guidance such as the 1st 100 Minutes Guidance for responding to an RDD, and allows for real-time data-sharing with assets such as FRMAC, NARAC, and DTRA. ", "20px 'Roboto Condensed'", "#FFFFFF");
        this.text_1.lineHeight = 28;
        this.text_1.lineWidth = 241;
        this.text_1.parent = this;
        this.text_1.setTransform(-120.15, -4.2);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.text_1 }, { t: this.text }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rrText, new cjs.Rectangle(-123, -175.4, 246, 350.8), null);


    (lib.rrDropdown = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#86BE39").s().p("A5OD2IAAnrMAydAAAIAAHrg");
        this.shape.setTransform(0.025, 0);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rrDropdown, new cjs.Rectangle(-161.5, -24.6, 323.1, 49.2), null);


    (lib.imTextBox = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#003461").s().p("A5OD2IAAnrMAydAAAIAAHrg");
        this.shape.setTransform(-161.5, -24.6, 1, 1, 0, 0, 0, -161.5, -24.6);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.imTextBox, new cjs.Rectangle(-161.5, -24.6, 323.1, 49.2), null);


    (lib.imText = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.text = new cjs.Text("         The Interagency Modeling and Atmospheric Assessment Center (IMAAC) model manager allows users to request support for an incident, view a library of previous IMAAC products, and add IMAAC products to specific events. ", "21px 'Roboto Condensed'", "#FFFFFF");
        this.text.lineHeight = 30;
        this.text.lineWidth = 256;
        this.text.parent = this;
        this.text.setTransform(-127.9, -162.1);

        this.timeline.addTween(cjs.Tween.get(this.text).wait(1));

    }).prototype = getMCSymbolPrototype(lib.imText, new cjs.Rectangle(-129.9, -164.1, 259.8, 328.2), null);


    (lib.crTextBox = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#EF9700").s().p("A5OD2IAAnrMAydAAAIAAHrg");
        this.shape.setTransform(0.025, 0);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.crTextBox, new cjs.Rectangle(-161.5, -24.6, 323.1, 49.2), null);


    (lib.crText = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.text = new cjs.Text("ChemResponder is a chemical incident and preparedness service for sharing real-time response data, including chemical readings, facility information, and contamination models. \n\nThe HAZMAT Incident Database facilitates analysis of incident trends. Organizations can also create and share \"lessons learned\".", "20px 'Roboto Condensed'", "#FFFFFF");
        this.text.lineHeight = 28;
        this.text.lineWidth = 242;
        this.text.parent = this;
        this.text.setTransform(-121, -183.25);

        this.timeline.addTween(cjs.Tween.get(this.text).wait(1));

    }).prototype = getMCSymbolPrototype(lib.crText, new cjs.Rectangle(-123, -185.2, 246, 370.5), null);


    (lib.brTextBox = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#AC2B3A").s().p("A5OD2IAAnrMAydAAAIAAHrg");
        this.shape.setTransform(0.025, 0);

        this.timeline.addTween(cjs.Tween.get(this.shape).wait(1));

    }).prototype = getMCSymbolPrototype(lib.brTextBox, new cjs.Rectangle(-161.5, -24.6, 323.1, 49.2), null);


    (lib.brText = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.text = new cjs.Text("UNDER DEVELOPMENT\n\nBioResponder will be an effective, national bioincident data management service intended to collect, aggregate and share critical information such as biological samples and laboratory analysis results using a standard format. \n\nJust as RadResponder and ChemResponder can be used by over 1500 organizations nationwide to view a common event space to prepare for and respond to radiological / chemical threats, BioResponder will help to network distributed FSLTT organizations to maintain holistic situational awareness of future and emerging bioincidents.", "18px 'Roboto Condensed'", "#FFFFFF");
        this.text.lineHeight = 26;
        this.text.lineWidth = 313;
        this.text.parent = this;
        this.text.setTransform(-145.5, -178.7, 0.9303, 0.9303);

        this.timeline.addTween(cjs.Tween.get(this.text).wait(1));

    }).prototype = getMCSymbolPrototype(lib.brText, new cjs.Rectangle(-147.3, -180.5, 294.70000000000005, 361.1), null);


    (lib._rrPillPrimative = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { open: 1, opened: 9, close: 10 });

        // timeline functions:
        this.frame_0 = function () {
            this.stop();
        }
        this.frame_1 = function () {
            // console.log('expand');
        }
        this.frame_9 = function () {
            // console.log('opened');
            this.stop();
        }
        this.frame_18 = function () {
            this.stop();
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(1).call(this.frame_1).wait(8).call(this.frame_9).wait(9).call(this.frame_18).wait(1));

        // shape
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AAAbvQreAAoJoJQoHoHAArfQAAreIHoJQIJoHLeAAIBEABQKyAVHwHxQIJIJAALeQAALfoJIHQn2H6rAAPg");

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("ApDbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAITKABQKyAVHxHxQIJIJAALeQAALfoJIHQn2H6rBAPg");
        this.shape_1.setTransform(57.9906, 0);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("AyHbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAMAlSAABQKyAVHxHxQIJIJAALeQAALfoJIHQn2H6rBAPg");
        this.shape_2.setTransform(115.9813, 0);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFFFFF").s().p("A7KbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAMA3ZAABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_3.setTransform(173.9719, 0);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FFFFFF").s().p("EgkOAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBJFAAAAAcAABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_4.setTransform(231.9625, 0);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FFFFFF").s().p("EgtSAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBbPAAAAAZAABQKyAVHxHxQIJIJAALeQAALgoJIGQn2H6rBAPg");
        this.shape_5.setTransform(289.9531, 0);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#FFFFFF").s().p("Eg2WAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBtYAAAAAYAABQKyAVHxHxQIJIJAALeQAALgoJIGQn2H6rBAPg");
        this.shape_6.setTransform(347.9438, 0);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FFFFFF").s().p("Eg/ZAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUB/gAAAAAXAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_7.setTransform(405.9344, 0);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FFFFFF").s().p("EhIdAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUCRqAAAAAVAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_8.setTransform(463.925, 0);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape }] }).to({ state: [{ t: this.shape }] }, 1).to({ state: [{ t: this.shape_1 }] }, 1).to({ state: [{ t: this.shape_2 }] }, 1).to({ state: [{ t: this.shape_3 }] }, 1).to({ state: [{ t: this.shape_4 }] }, 1).to({ state: [{ t: this.shape_5 }] }, 1).to({ state: [{ t: this.shape_6 }] }, 1).to({ state: [{ t: this.shape_7 }] }, 1).to({ state: [{ t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_7 }] }, 1).to({ state: [{ t: this.shape_6 }] }, 1).to({ state: [{ t: this.shape_5 }] }, 1).to({ state: [{ t: this.shape_4 }] }, 1).to({ state: [{ t: this.shape_3 }] }, 1).to({ state: [{ t: this.shape_2 }] }, 1).to({ state: [{ t: this.shape_1 }] }, 1).to({ state: [{ t: this.shape }] }, 1).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-177.5, -177.5, 1282.8, 355);


    (lib._crPillPrimative = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { "open": 1, "opened": 9, "close": 10 });

        // timeline functions:
        this.frame_0 = function () {
            this.stop();
        }
        this.frame_9 = function () {
            this.stop();
        }
        this.frame_18 = function () {
            this.stop();
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(9).call(this.frame_9).wait(9).call(this.frame_18).wait(1));

        // shape
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AAAbvQreAAoJoJQoHoHAArfQAAreIHoJQIJoHLeAAIBEABQKyAVHwHxQIJIJAALeQAALfoJIHQn2H6rAAPg");

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("AqTbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAIVrABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_1.setTransform(66.0531, 0);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("A0obvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAMAqUAABQKyAVHxHxQIJIJAALeQAALfoJIHQn2H6rBAPg");
        this.shape_2.setTransform(132.1063, 0);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFFFFF").s().p("A+8bvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAMA+9AABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_3.setTransform(198.1594, 0);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FFFFFF").s().p("EgpRAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBTLAAAAAbAABQKyAVHxHxQIJIJAALeQAALfoJIHQn2H6rBAPg");
        this.shape_4.setTransform(264.2125, 0);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FFFFFF").s().p("EgzlAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBn1AAAAAaAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_5.setTransform(330.2656, 0);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#FFFFFF").s().p("Eg95AbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUB8eAAAAAZAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_6.setTransform(396.3188, 0);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FFFFFF").s().p("EhIOAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUCRJAAAAAXAABQKyAVHxHxQIJIJAALeQAALgoJIGQn2H6rBAPg");
        this.shape_7.setTransform(462.3719, 0);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FFFFFF").s().p("EhSiAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUClzAAAAAWAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_8.setTransform(528.425, 0);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape }] }).to({ state: [{ t: this.shape }] }, 1).to({ state: [{ t: this.shape_1 }] }, 1).to({ state: [{ t: this.shape_2 }] }, 1).to({ state: [{ t: this.shape_3 }] }, 1).to({ state: [{ t: this.shape_4 }] }, 1).to({ state: [{ t: this.shape_5 }] }, 1).to({ state: [{ t: this.shape_6 }] }, 1).to({ state: [{ t: this.shape_7 }] }, 1).to({ state: [{ t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_7 }] }, 1).to({ state: [{ t: this.shape_6 }] }, 1).to({ state: [{ t: this.shape_5 }] }, 1).to({ state: [{ t: this.shape_4 }] }, 1).to({ state: [{ t: this.shape_3 }] }, 1).to({ state: [{ t: this.shape_2 }] }, 1).to({ state: [{ t: this.shape_1 }] }, 1).to({ state: [{ t: this.shape }] }, 1).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-177.5, -177.5, 1411.8, 355);


    (lib._brPillPrimative = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { "open": 1, "opened": 9, "close": 10 });

        // timeline functions:
        this.frame_0 = function () {
            this.stop();
        }
        this.frame_9 = function () {
            this.stop();
        }
        this.frame_18 = function () {
            this.stop();
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(9).call(this.frame_9).wait(9).call(this.frame_18).wait(1));

        // shape
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FFFFFF").s().p("AAAbvQreAAoJoJQoHoHAArfQAAreIHoJQIJoHLeAAIBEABQKyAVHwHxQIJIJAALeQAALfoJIHQn2H6rAAPg");

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("Ao1bvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAISvABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_1.setTransform(56.6469, 0);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("AxsbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAMAkcAABQKyAVHxHxQIJIJAALeQAALfoJIHQn2H6rBAPg");
        this.shape_2.setTransform(113.2938, 0);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FFFFFF").s().p("A6ibvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAMA2JAABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_3.setTransform(169.9406, 0);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FFFFFF").s().p("EgjYAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBHZAAAAAcAABQKyAVHwHxQIJIJAALeQAALfoJIHQn1H6rBAPg");
        this.shape_4.setTransform(226.5875, 0);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FFFFFF").s().p("EgsPAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBZIAAAAAaAABQKyAVHxHxQIJIJAALeQAALgoJIGQn2H6rBAPg");
        this.shape_5.setTransform(283.2344, 0);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#FFFFFF").s().p("Eg1FAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUBq2AAAAAZAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_6.setTransform(339.8813, 0);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FFFFFF").s().p("Eg97AbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUB8kAAAAAXAABQKyAVHwHxQIJIJAALeQAALgoJIGQn1H6rBAPg");
        this.shape_7.setTransform(396.5281, 0);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FFFFFF").s().p("EhGyAbvQrfAAoJoJQoHoHAArfQAAreIHoJQIJoHLfAAUCOTAAAAAVAABQKyAVHxHxQIJIJAALeQAALgoJIGQn2H6rBAPg");
        this.shape_8.setTransform(453.175, 0);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape }] }).to({ state: [{ t: this.shape }] }, 1).to({ state: [{ t: this.shape_1 }] }, 1).to({ state: [{ t: this.shape_2 }] }, 1).to({ state: [{ t: this.shape_3 }] }, 1).to({ state: [{ t: this.shape_4 }] }, 1).to({ state: [{ t: this.shape_5 }] }, 1).to({ state: [{ t: this.shape_6 }] }, 1).to({ state: [{ t: this.shape_7 }] }, 1).to({ state: [{ t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_8 }] }, 1).to({ state: [{ t: this.shape_7 }] }, 1).to({ state: [{ t: this.shape_6 }] }, 1).to({ state: [{ t: this.shape_5 }] }, 1).to({ state: [{ t: this.shape_4 }] }, 1).to({ state: [{ t: this.shape_3 }] }, 1).to({ state: [{ t: this.shape_2 }] }, 1).to({ state: [{ t: this.shape_1 }] }, 1).to({ state: [{ t: this.shape }] }, 1).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-177.5, -177.5, 1261.3, 355);


    (lib.white_tab = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // white_tab_path
        this.instance = new lib.white_tab_path();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.5304, 0.5304, 0, 0, 0, 159, 144.1);
        this.instance.shadow = new cjs.Shadow("rgba(204,204,204,1)", 0, 4, 0);

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

        // _Path_
        this.instance_1 = new lib.white_tab_path();
        this.instance_1.parent = this;
        this.instance_1.setTransform(0, 0, 0.5304, 0.5304, 0, 0, 0, 159, 144.1);
        this.instance_1.shadow = new cjs.Shadow("rgba(0,0,0,0.349)", 0, 14, 43);

        this.timeline.addTween(cjs.Tween.get(this.instance_1).wait(1));

    }).prototype = getMCSymbolPrototype(lib.white_tab, new cjs.Rectangle(-260, -93, 371, 218), null);


    (lib.white_circle = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.Path();
        this.instance.parent = this;
        this.instance.setTransform(0, 0, 0.5304, 0.5304, 0, 0, 0, 159, 144.1);
        this.instance.shadow = new cjs.Shadow("rgba(204,204,204,1)", 0, 4, 0);

        this.instance_1 = new lib.Path();
        this.instance_1.parent = this;
        this.instance_1.setTransform(0, 0, 0.5304, 0.5304, 0, 0, 0, 159, 144.1);
        this.instance_1.shadow = new cjs.Shadow("rgba(0,0,0,0.349)", 0, 14, 43);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.instance_1 }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.white_circle, new cjs.Rectangle(-107, -93, 218, 218), null);


    (lib.rr_text_box = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.rrTextBox = new lib.rrDropdown();
        this.rrTextBox.name = "rrTextBox";
        this.rrTextBox.parent = this;
        this.rrTextBox.setTransform(-178, -37, 1, 1, 0, 0, 0, -178, -37);

        this.timeline.addTween(cjs.Tween.get(this.rrTextBox).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-161.5, -24.6, 323.1, 49.2);


    (lib.rr_text = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.rrText = new lib.rrText();
        this.rrText.name = "rrText";
        this.rrText.parent = this;
        this.rrText.setTransform(-59, -90.2, 1, 1, 0, 0, 0, -182, -265.6);

        this.timeline.addTween(cjs.Tween.get(this.rrText).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rr_text, new cjs.Rectangle(0, 0, 246, 350.8), null);


    (lib.rr_svgsvg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // rr_svg_svg
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#E1E1E1").s().p("Ag2A3IAAAAIgDgDQgUgXAAgeQABgfAWgWQAXgXAfAAQAgAAAXAXQAXAXAAAfQABAdgVAXIgDADQgXAXggAAQgfAAgXgXg");
        this.shape.setTransform(116.9267, 117.175);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#B42E3B").s().p("Ah4guQgKgZAAgbQAAg2AmgnQAmgmA2AAIAAAAQA2AAAmAmQAmAnAAA2QABAbgLAZIh4EUgAg2iZQgXAWAAAhQgBAdAVAXIADADQAXAWAfAAQAgAAAXgWIAAAAIACgDQAVgYgBgeQgBgfgWgWQgWgXggAAQgfAAgXAXg");
        this.shape_1.setTransform(116.953, 127.1);

        this.instance = new lib.ClipGroup();
        this.instance.parent = this;
        this.instance.setTransform(192.1, 176.6, 1, 1, 0, 0, 0, 192.1, 176.6);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#67C023").s().p("AiqFpIAAoSQABgLgGgJQgHgJgMgBIgHAAIg3AAIAAiXIChAAQBfAAAABWIAAAqQAAAXgCAWIACAAQAZhPA6gzQA/g1BPAAQAPAAASADIAACuQgYgDgUABQg+AAgzAjQg6AngXBLQgPA6AAA5IAAEag");
        this.shape_2.setTransform(1185.9, 212.775);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#67C023").s().p("AjqEGQhnhpAAidQAAijBkhpQBlhoCfABQCUAABWBhQBRBdAACTQAAAVgEAsInqAAQAKBbA9AzQA6AxBTAAQBIAABHgoQAkgUAVgUIBKB7Qg7A0hKAbQhKAchPAAQirAAhrhugAhajFQguAngPBDIE0AAQgCg/gngpQgmgog3AAQhDAAguAmg");
        this.shape_3.setTransform(1123.375, 213.25);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#67C023").s().p("Ak1GQQhXhmAAinQABinBbhnQBZhlCQAAQBgAAA/ArQAgAVANAWIACAAQgBgTAAgXIAAh9QACgMgIgJQgGgJgMgCQgDgBgDABIg5AAIAAiWICpAAQAxAAAXAXQAYAXAAAxIAALLQgCALAHAKQAHAIAMACQADABADgBIA2AAIAACVIijAAQhYAAAAhJIAAgaIgDAAQgOAdgkAdQhGA6hlAAQiQAAhWhogAiigfQg1A8gBBmQABBlAwA9QAxA+BPAAQBKAAAwg1QA3g9AAhvQAAhagrg8QgxhFhSAAQhNAAgxA6g");
        this.shape_4.setTransform(1049.9, 200.15);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#67C023").s().p("AD9FtQgzgBgWgWQgWgWAAgzIAAlFQAAhIgWgiQgbgog/AAQhFAAg0AoQgyAogTBCQgPAtABAwIAAFHIiwAAIAAoRQABgLgGgKQgHgIgLgCIgIAAIg3AAIAAiXICjAAQAuAAAYAVQAXAVAAAmIAAAVQAAAPgDAQIADAAQAeg6A2gnQBHg0BfABQD5AAAAEPIAAEVQgBALAHAJQAIAJALABIAFAAIA4AAIAACWg");
        this.shape_5.setTransform(966.7, 212.45);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#67C023").s().p("AkUEMQhxhqgBiiQAAihBzhrQBvhoCkAAQClAABwBpQBxBqAAChQAACihxBqQhwBpilAAQilAAhvhpgAiVifQg+A/AABgQAABgA/BAQA9A+BXAAQBYAAA9g+QA+g/AAhhQAAhhg+g/Qg9g9hYAAQhYAAg9A+g");
        this.shape_6.setTransform(885.7, 213.275);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#67C023").s().p("Ak1H4IAAspQACgLgIgJQgGgJgMgBIgGAAIg5AAIAAiXICfAAQBYAAABBEIAAAcIACAAIAMgRQARgWAXgSQBHg4BmAAQCQAABYBoQBVBmAACmQAACmhdBpQhbBmiOAAQhXAAhDgvQgUgPgRgSIgMgOIgDAAQADAeAAAfIAAEngAhSkpQg3A8AABvQAABaAsA8QAxBEBTAAQBMAAAyg5QA2g9AAhlQgBhlgxg9Qgxg+hPAAQhKAAgxA2g");
        this.shape_7.setTransform(802.7, 226.4);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#67C023").s().p("AjRE2QgfgUgZgYIgTgUIBUh0IA9A1QBLA0BLAAQArAAAagRQAcgTAAgiQAAggg7geQhRgigvgUQhWgmgrgoQg7g5AAhOQAAhmBTg4QBLgyB2AAQBcAABFAeQBZAoAABOIAABKIidAAIAAgiQAAgYgegOQgagMghAAQgwAAgcAQQgdARAAAfQAAAkA7AdQAiARBfAiQBXAjArAnQA7A3AABRQAABihPA9QhMA6h3AAQh5AAhjg/g");
        this.shape_8.setTransform(732.325, 213.225);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#67C023").s().p("AjrEGQhnhpAAidQABikBlhoQBlhoCeAAQCUAABWBiQBSBdAACTQAAAVgFAsInqAAQAKBbA+AzQA6AxBTAAQBHAABIgoQAjgUAWgUIBJB7Qg8A0hJAcQhKAchPAAQirAAhshvgAhajFQguAmgOBEIEzAAQgChAgngoQgmgog3AAQhDAAguAmg");
        this.shape_9.setTransform(666.525, 213.275);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#67C023").s().p("AE6HwQg5AAgbgOQgfgPgXgtIh/j6QgPgdgUgJQgSgIgoAAIhbAAIAAFyIi4AAIAAtEIhaAAIAAibIG7AAQCKAABUBPQBWBSAACKQAAB1hCBNQgyA4gxAKIAAADQAhARAQAhIBgC/QALAVASAHQAOAFAcAAIASAAIAACbgAiIgeICQAAQBGAAAmgpQAmgpAAhKQAAhKgmgoQgmgohDAAIiTAAg");
        this.shape_10.setTransform(592.35, 199.325);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#0071AB").s().p("Ak1GQQhXhmAAinQAAinBchnQBahlCOAAQBhAAA/ArQAgAVAMAWIAEAAQgCgTAAgXIAAh9QABgMgGgJQgIgJgKgCIgHAAIg4AAIAAiWICpAAQAwAAAYAXQAWAXAAAxIAALLQgBALAIAKQAGAIAMACQADABADgBIA3AAIAACVIikAAQhXAAAAhJIAAgaIgEAAQgOAdgjAdQhHA6hkAAQiQAAhXhogAijgfQg0A8AABmQAABlAxA9QAwA+BPAAQBKAAAxg1QA3g9AAhvQAAhagsg8QgwhFhUAAQhMAAgyA6g");
        this.shape_11.setTransform(509.4, 200.15);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#0071AB").s().p("AkYE7QhKg/AAhlQAAiRCog7QBpglCVAAIAXAAIAAgGQAAhLggghQgegfhEAAQgiAAgaAJQgjAMAAAaIAAAmIilAAIAAhLQAAhXBngkQBBgXBdAAQCmAABMBSQBABFAACEIAAEIQgBALAHAKQAIAIALACQADABADgBIA4AAIAACVIigAAQguAAgYgXQgUgUAAgjIABgfIgDAAQgNAggiAfQhCA/hlgBQhkABhFg6gAivCPQAAAmAbAaQAdAbA0AAQBDABAwg+QAug5AAhFIAAgQIgfAAQjuAAAABwg");
        this.shape_12.setTransform(432.775, 213.25);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f("#0071AB").s().p("AE6HwQg5AAgbgOQgfgPgXgtIh/j6QgPgdgUgJQgSgIgoAAIhbAAIAAFyIi3AAIAAtEIhcAAIAAibIG8AAQCKAABUBPQBWBSAACKQAAB1hCBNQgxA4gxAKIAAADQAfARARAhIBhC/QAKAVASAHQAPAFAbAAIASAAIAACbgAiIgeICQAAQBFAAAngpQAngpAAhKQAAhKgngoQglgohEAAIiTAAg");
        this.shape_13.setTransform(352.85, 199.325);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_13 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.instance }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(0, 0, 1211.7, 353.2);


    (lib.reg_button2 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.reg_internal_button();
        this.instance.parent = this;

        this.timeline.addTween(cjs.Tween.get(this.instance).to({ scaleX: 1.0796, scaleY: 1.0796 }, 1).to({ scaleX: 1, scaleY: 1 }, 1).wait(2));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-128.9, -41.2, 257.9, 82.5);


    (lib.im_text_box = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.imTextBox = new lib.imTextBox();
        this.imTextBox.name = "imTextBox";
        this.imTextBox.parent = this;
        this.imTextBox.setTransform(-161.55, -24.6, 1.0433, 1, 0, 0, 0, -161.5, -24.6);

        this.timeline.addTween(cjs.Tween.get(this.imTextBox).wait(1));

    }).prototype = getMCSymbolPrototype(lib.im_text_box, new cjs.Rectangle(-161.5, -24.6, 337, 49.2), null);


    (lib.im_text = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.imText = new lib.imText();
        this.imText.name = "imText";
        this.imText.parent = this;
        this.imText.setTransform(-33.6, -47.9, 1, 1, 0, 0, 0, -168.7, -206.8);

        this.timeline.addTween(cjs.Tween.get(this.imText).wait(1));

    }).prototype = getMCSymbolPrototype(lib.im_text, new cjs.Rectangle(5.2, -5.2, 259.8, 328.2), null);


    (lib.ClipGroup_14_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AhTBVQgigjgBgyQABgwAigjQAjgkAwAAQAxAAAjAkQAjAjgBAwQABAygjAjQgjAjgxAAQgwAAgjgjg");
        mask.setTransform(11.85, 11.975);

        // Layer_3
        this.instance = new lib.ClipGroup_0();
        this.instance.parent = this;
        this.instance.setTransform(11.8, 12, 1, 1, 0, 0, 0, 11.8, 12);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_14_1, new cjs.Rectangle(0, 0, 23.7, 24), null);


    (lib.ClipGroup_13_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AhRBSQgigiAAgwQAAgvAigiQAigiAvAAQAwAAAiAiQAiAiAAAvQAAAwgiAiQgiAigwAAQgvAAgigig");
        mask.setTransform(11.575, 11.575);

        // Layer_3
        this.instance = new lib.ClipGroup_1();
        this.instance.parent = this;
        this.instance.setTransform(11.6, 11.6, 1, 1, 0, 0, 0, 11.6, 11.6);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_13_1, new cjs.Rectangle(0, 0, 23.2, 23.2), null);


    (lib.ClipGroup_12_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AhOBTQgigdgEgrIAAgUQAEgrAigeQAhgdAtAAQAwAAAjAhQAiAhAAAtQAAAugiAhQgjAhgwAAQgtAAghgdg");
        mask.setTransform(11.675, 11.7);

        // Layer_3
        this.instance = new lib.ClipGroup_2();
        this.instance.parent = this;
        this.instance.setTransform(11.7, 11.7, 1, 1, 0, 0, 0, 11.7, 11.7);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_12_1, new cjs.Rectangle(0, 0.5, 23.4, 22.4), null);


    (lib.ClipGroup_11_1 = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("AhSBQQgigiAAguQAAguAiggQAjghAvAAQAwAAAjAhQAiAgAAAuQAAAugiAiQgjAggwAAQgvAAgjggg");
        mask.setTransform(12.85, 12.2);

        // Layer_3
        this.instance = new lib.ClipGroup_3();
        this.instance.parent = this;
        this.instance.setTransform(12.8, 11.7, 1, 1, 0, 0, 0, 12.8, 11.7);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1));

    }).prototype = getMCSymbolPrototype(lib.ClipGroup_11_1, new cjs.Rectangle(1.2, 1, 23.400000000000002, 22.4), null);


    (lib.cr_text_box = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.crTextBox = new lib.crTextBox();
        this.crTextBox.name = "crTextBox";
        this.crTextBox.parent = this;
        this.crTextBox.setTransform(-161.5, -24.6, 1, 1, 0, 0, 0, -161.5, -24.6);

        this.timeline.addTween(cjs.Tween.get(this.crTextBox).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cr_text_box, new cjs.Rectangle(-161.5, -24.6, 323.1, 49.2), null);


    (lib.cr_text = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.crText = new lib.crText();
        this.crText.name = "crText";
        this.crText.parent = this;
        this.crText.setTransform(-41, -98.15, 1, 1, 0, 0, 0, -164, -283.4);

        this.timeline.addTween(cjs.Tween.get(this.crText).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cr_text, new cjs.Rectangle(0, 0, 246, 370.6), null);


    (lib.ChemResponder_logo__blueorangesvgcopy = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FF9900").s().p("AiqFpIAAoRQACgMgIgIQgGgKgLgBIgHAAIg5AAIAAiXICjAAQAvAAAXAVQAYAVAAAsIAAAqQAAAagCATIACAAQAYhQA7gxQA/g2BPAAIAhADIAACuQgWgDgWAAQg+AAgzAjQg6ApgXBKQgPA6AAA5IAAEag");
        this.shape.setTransform(1292.1, 129.45);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FF9900").s().p("AAsF0QirAAhrhuQhohpABidQgBikBlhoQBlhoCeAAQCUAABXBjQBSBcgBCTQABAVgFAsInrAAQALBbA9A0QA6AwBTAAQBIAABHgoQAkgUAVgUIBKB7Qg8A0hJAcQhJAchNAAIgDgBgAhbjEQgtAmgOBDIEzAAQgCg/gngpQgmgog4AAQhCABgvAmg");
        this.shape_1.setTransform(1229.5999, 129.9004);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FF9900").s().p("Ak1GQQhWhmAAinQAAinBbhnQBahmCOAAQBgAABAAsQAgAWANAVIACAAIgBgqIAAh9QACgLgHgKQgHgJgMgBQgDgBgEABIg4AAIAAiXICpAAQAxAAAXAXQAXAXAAAxIAALLQgBAMAHAJQAIAJALABQADABADgBIA2AAIAACVIijAAQhYAAAAhIIAAgbIgDAAQgOAdgjAdQhHA6hlAAQiPAAhXhogAijgfQg0A8AABmQAABlAwA+QAxA8BOAAQBLAAAwg1QA3g8AAhvQAAhagrg8QgxhEhTAAQhMAAgyA5g");
        this.shape_2.setTransform(1156.125, 116.825);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FF9900").s().p("AD9FsQgyABgXgXQgWgWAAgzIAAlGQAAhHgXgiQgagog/AAQhFAAg0AoQgyAogTBBQgPAwABAvIAAFGIiwAAIAAoSQABgLgHgIQgGgKgMgBQgDgBgEABIg4AAIAAiXICkAAQAuAAAYAVQAXAUAAAnIAAAVQAAAPgDAPIADAAQAeg6A1gmQBIg0BfAAQD5AAAAEPIAAEVQgBALAHAKQAIAJALABIA+AAIAACVg");
        this.shape_3.setTransform(1072.925, 129.15);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FF9900").s().p("AkUENQhxhqgBijQAAihBzhqQBvhpCkAAQClAABwBpQBxBrABCgQgBCihxBqQhwBpilAAQilAAhvhogAiUifQg+A/AABgQAABhA+A/QA9A+BXAAQBZAAA8g9QA/g/AAhiQAAhgg/g/Qg8g+hZAAQhXAAg9A+g");
        this.shape_4.setTransform(990.9, 129.925);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FF9900").s().p("Ak1H4IAAspQACgLgHgJQgHgJgLgCIgIAAIg3AAIAAiXICdAAQBaAAAABFIAAAcIACAAIAMgSQARgVAWgRQBIg5BnAAQCPAABXBoQBWBmAACnQAACmhdBoQhbBliOAAQhXAAhDguQgVgPgQgRIgMgPIgDAAQADAfAAAeIAAEngAhSkqQg3A8AABvQAABaAsA8QAwBFBTAAQBMAAAyg6QA2g8AAhmQAAhlgyg+Qgwg8hQAAQhKAAgwA1g");
        this.shape_5.setTransform(906.925, 143.1);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#FF9900").s().p("AjRE1QgfgUgZgXIgTgUIBUh0IA8A0QBMA1BLAAQArAAAagRQAbgTAAgiQAAggg6geQhSgigugUQhXgmgrgoQg6g5AAhOQAAhlBTg4QBKgzB2AAQBdAABEAfQBZAnAABOIAABKIicAAIAAgjQAAgXgegOQgagNgiAAQgvAAgcARQgeARAAAfQAAAkA7AcQAjASBfAiQBXAjArAnQA7A3AABQQAABjhPA8QhMA7h3AAQh5AAhjhAg");
        this.shape_6.setTransform(836.575, 129.925);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FF9900").s().p("AAsF0QirAAhrhuQhnhpAAidQgBikBlhoQBlhoCfAAQCUAABWBjQBRBcAACTQAAAVgEAsInqAAQAKBbA9A0QA6AwBTAAQBIAABHgoQAkgUAVgUIBKB7Qg7A0hKAcQhJAchNAAIgDgBgAhajEQguAmgPBDIE0AAQgCg/gngpQgmgog3AAQhDABguAmg");
        this.shape_7.setTransform(770.7749, 129.9004);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FF9900").s().p("AE6HvQg6AAgbgNQgegPgXguIh/j6QgPgcgUgKQgSgIgnAAIhdAAIAAFyIi3AAIAAtDIhaAAIAAibIG6AAQCLAABUBPQBWBSAACKQAAB1hDBNQgxA4gxAKIAAADQAgARARAhIBgC/QALAUATAIQANAEAbAAIASAAIAACbgAiJgeICRAAQBFAAAngpQAngpAAhKQAAhKgngoQglgohEAAIiUAAg");
        this.shape_8.setTransform(696.6, 116);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#0071AB").s().p("AHPFsQgyABgXgXQgWgWAAgzIAAlIQAAhGgSggQgXgpg5AAQg/AAgvAwQgtAtgPBGQgMArABAvIAAE5IixAAIAAmnQAAhFgQgfQgVgrg7AAQhAAAgvAwQgqAtgTBIQgLApAAAvIAAE5IivAAIAAoSQABgLgHgIQgGgKgMgBIg/AAIAAiXICjAAQAuAAAZAVQAXAUgBAnIAAAVIgBAeIADAAQAcg7A6gpQBDgvBNgBQCgAAAvCTIADAAQAjhDBCgoQBBgoBNAAQDpAAAAEPIAAEVQgBALAHAKQAHAJAMABIA8AAIAACVg");
        this.shape_9.setTransform(587.65, 129.15);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#0071AB").s().p("AAsF0QirAAhrhuQhnhpAAidQgBikBlhoQBlhoCfAAQCUAABWBjQBRBcAACTQAAAVgEAsInqAAQAKBbA9A0QA6AwBTAAQBIAABHgoQAkgUAVgUIBKB7Qg7A0hKAcQhJAchNAAIgDgBgAhajEQguAmgPBDIE0AAQgCg/gngpQgmgog3AAQhDABguAmg");
        this.shape_10.setTransform(488.8749, 129.9004);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#0071AB").s().p("AD9HvQgyAAgWgWQgWgWAAgyIAAlHQAAhHgXghQgbgpg/AAQhEAAg1AqQgyAogVBCQgMAuABAtIAAFHIixAAIAAsoQABgLgGgKQgIgIgKgCQgEgBgEABIg3AAIAAiXICpAAQAwABAYAWQAWAWAAAxIAAD5QAAAggCAcIACAAQAcg4A9gnQBFgtBVAAQD5AAAAEOIAAEVQgBAMAHAJQAIAJALABQADABADgBIA4AAIAACVg");
        this.shape_11.setTransform(413.3, 116);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#0071AB").s().p("AA3IAQjfAAiQiXQiNiTAAjeQAAjXCQiQQCPiQDWAAQCAAABsAvQCMA/AABwIAABuIimAAIAAg3QAAgzhGgeQg8gahLAAQiHAAhZBZQhcBdAACYQAACRBdBmQBeBmCGAAQB1AABnhBQA0giAcghIBgCEQhOBRhoAtQhnAshvAAIgDAAg");
        this.shape_12.setTransform(324.975, 115.9503);

        this.pin_2 = new lib.pin_2_1();
        this.pin_2.name = "pin_2";
        this.pin_2.parent = this;
        this.pin_2.setTransform(191.4, 203.15, 0.22, 0.22, 0, 180, 0);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f().s("#FFFFFF").ss(8, 1).p("AjqGpIHVtR");
        this.shape_13.setTransform(107.05, 180.3);

        this.instance = new lib.ClipGroup_13();
        this.instance.parent = this;
        this.instance.setTransform(194.5, 191.4, 1, 1, 0, 0, 0, 194.5, 191.4);

        this.shape_14 = new cjs.Shape();
        this.shape_14.graphics.f("#FF9900").s().p("AgxAyQgUgVAAgdQAAgcAUgUQAVgVAcAAQAdAAAUAVQAVAUAAAcQAAAdgVAVQgTAUgeAAQgcAAgVgUg");
        this.shape_14.setTransform(138.55, 108.8);

        this.shape_15 = new cjs.Shape();
        this.shape_15.graphics.f("#FF9900").s().p("AhFBHQgegeAAgpQAAgoAegeQAdgdAoAAQAqAAAdAdQAdAeAAAoQAAApgdAeQgdAdgqAAQgoAAgdgdg");
        this.shape_15.setTransform(161.55, 80.8);

        this.shape_16 = new cjs.Shape();
        this.shape_16.graphics.f("#FF9900").s().p("Ag+BAQgbgaAAgmQAAgkAbgbQAagaAkAAQAmAAAaAaQAaAbAAAkQAAAmgaAaQgaAagmAAQgkAAgagag");
        this.shape_16.setTransform(147.55, 45.8);

        this.shape_17 = new cjs.Shape();
        this.shape_17.graphics.f("#E1E1E1").s().p("AsSTUQgxAAgqgYQhCglgVhKQgUhJAlhDIKeyjIAAtmIgCAAQgdgBgUgVQgUgVABgdQACgdAVgUQAVgUAdABIIlAAQAdgBAVAUQAVAUACAcIAAADQAAAdgVAUQgUAVgdgBIAANmIKeSlQAYAqAAAxQAABMg2A2Qg2A2hMAAg");
        this.shape_17.setTransform(152.3491, 124.5244);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_17 }, { t: this.shape_16 }, { t: this.shape_15 }, { t: this.shape_14 }, { t: this.instance }, { t: this.shape_13 }, { t: this.pin_2 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(0, 0, 1317.9, 382.9);


    (lib.chem_svg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.pin2 = new lib.pin_2();
        this.pin2.name = "pin2";
        this.pin2.parent = this;
        this.pin2.setTransform(136.25, 202.35, 0.22, 0.22, 0, 180, 0);

        this.shape = new cjs.Shape();
        this.shape.graphics.f().s("#FFFFFF").ss(8, 1).p("AjqGpIHVtR");
        this.shape.setTransform(51.9, 179.5);

        this.instance = new lib.ClipGroup_12();
        this.instance.parent = this;
        this.instance.setTransform(139.35, 190.6, 1, 1, 0, 0, 0, 194.5, 191.4);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#F79900").s().p("AgxAyQgUgVAAgdQAAgcAUgUQAVgVAcAAQAdAAAUAVQAVAUAAAcQAAAdgVAVQgTAUgeAAQgcAAgVgUg");
        this.shape_1.setTransform(83.4, 108);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#F79900").s().p("AhFBHQgegeAAgpQAAgoAegeQAdgdAoAAQAqAAAdAdQAdAeAAAoQAAApgdAeQgdAdgqAAQgoAAgdgdg");
        this.shape_2.setTransform(106.4, 80);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#F79900").s().p("Ag+BAQgbgaAAgmQAAgkAbgbQAagaAkAAQAmAAAaAaQAaAbAAAkQAAAmgaAaQgaAagmAAQgkAAgagag");
        this.shape_3.setTransform(92.4, 45);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#E0E0E0").s().p("AsSTUQgxAAgqgYQhCglgVhKQgUhJAlhDIKeyjIAAtmIgCAAQgdgBgUgVQgUgVABgdQACgdAVgUQAVgUAdABIIlAAQAdgBAVAUQAVAUACAcIAAADQAAAdgVAUQgUAVgdgBIAANmIKeSlQAYAqAAAxQAABMg2A2Qg2A2hMAAg");
        this.shape_4.setTransform(97.1991, 123.7244);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.instance }, { t: this.shape }, { t: this.pin2 }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.chem_svg, new cjs.Rectangle(-55.1, -0.8, 388.90000000000003, 382.90000000000003), null);


    (lib.CBRNResponder_logo_19Jun2019_1svg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // svg65
        this.instance = new lib.path125();
        this.instance.parent = this;
        this.instance.setTransform(595.05, 606.1, 1, 1, 0, 0, 0, 146.4, 422);
        this.instance.alpha = 0.25;

        this.shape = new cjs.Shape();
        this.shape.graphics.f("#3770A7").s().p("EgppgPbIgZhEQjpoTAAp2QAAy5NXtcQNetXS2AAQS2AANfNXQNXNXAAS+QgBJ+jpILMgp9Bg0gEgKXg7pQk1CAj7D+Qn+H/AALOQAAKmG/HjQAeAhAhAeQH5H5LTAAQFnAAExh/QEvh+EAj8QAfgjAhgcQG+nrAAqeQAArMn5oBQn/n+rNAAQloAAk0CAg");
        this.shape.setTransform(741.025, 514.075);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("AqiY/Qk3iEjwjwQjxjxiDk3QiJlCAAlhQAAlgCJlCQCDk3DxjwQDwjxE3iDQFCiJFgAAQFhAAFCCJQE4CDDwDxQDwDwCDE3QCJFCAAFgQAAFhiJFCQiDE3jwDxQjwDwk4CEQlCCIlhAAQlgAAlCiIg");
        this.shape_1.setTransform(741.375, 293.2);

        this.instance_1 = new lib.polygon114();
        this.instance_1.parent = this;
        this.instance_1.setTransform(557.15, 774.15, 1, 1, 0, 0, 0, 184.3, 254.1);
        this.instance_1.alpha = 0.25;

        this.instance_2 = new lib.svg4();
        this.instance_2.parent = this;
        this.instance_2.setTransform(479.1, 807.3, 1, 1, 0, 0, 0, 142.9, 144.9);
        this.instance_2.alpha = 0.9492;

        this.instance_3 = new lib.g907();
        this.instance_3.parent = this;
        this.instance_3.setTransform(957.9, 1225.45, 1, 1, 0, 0, 0, 136.7, 135.6);
        this.instance_3.alpha = 0.9492;

        this.instance_4 = new lib.g1399();
        this.instance_4.parent = this;
        this.instance_4.setTransform(970.4, 799.95, 1, 1, 0, 0, 0, 151.7, 150.3);
        this.instance_4.alpha = 0.9492;

        this.instance_5 = new lib.g833();
        this.instance_5.parent = this;
        this.instance_5.setTransform(481.4, 1223.35, 1, 1, 0, 0, 0, 180.5, 89.4);
        this.instance_5.alpha = 0.9492;

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("EgOGBxHQnAi2lZlZMhOYhOYQlZlZi2nAQivmxAAnWQAAnUCvmxQC2nBFZlZMBOYhOYQFRlSG4i3QG5i2HdABQHdgBG5C2QG5C3FRFSMBOXBOYQFaFZC2HBQCvGxAAHUQAAHWivGxQi2HAlaFZMhOXBOYIAAAAQlaFZnAC2QmxCvnVAAQnVAAmxivgEgJ9hmaQkyB/jqDrMhOYBOXQjvDwh/E3Qh6EtABFFQgBFGB6EtQB/E4DvDvMBOYBOYIABAAQDwDwE3B+QEsB6FFAAQFGAAEth6QE3h/DvjvMBOYhOYQDwjvB+k4QB7ktAAlGQAAlFh7ktQh+k3jwjwMhOYhOXQjpjrkzh/Qkyh+lLAAIgCAAQlKAAkxB+g");
        this.shape_2.setTransform(741.4, 1028.1494);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#EF9700").s().p("Eg5lA5mMBzLhzKMAAABzKg");
        this.shape_3.setTransform(372.825, 659.6);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#AC2B3A").s().p("Eg5kA5mMAAAhzKMBzKBzKg");
        this.shape_4.setTransform(1109.95, 659.6);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#003461").s().p("Eg5lg5lMBzLAAAMAAABzLg");
        this.shape_5.setTransform(372.825, 1396.725);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#86BE39").s().p("Eg5kg5lMBzKAAAMhzKBzLg");
        this.shape_6.setTransform(1109.95, 1396.725);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#979796").s().p("AsFcgMAABgsoQAAidicAAIklAAIAApQIKQAAQDsAABxBmQByBmAADZIAADkQAACBgOB4IAOAAQCFmdEjj/QE5kQGSAAQBbABBeAOIAAKrQhkgVhrAAQlRgBkRDgQkZDmh4GKQhWETAAF1IAAXDg");
        this.shape_7.setTransform(4853.95, 1475.35);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#979796").s().p("AogbQQlciMkCkDQj/kAiJlXQiLldAAmNQAAmlCLllQCFlXD4j8QD1j4FKiFQFNiFF+AAQFxAAEsCEQEcB+DLDsQDCDkBnE1QBkExAAFhQAABcgWDAMgpEAAAQAPEZBqDoQBlDbCuCeQCnCZDaBRQDYBQDxAAQGEAAF1jSQC5hqBthpIErHsQiFCEjwCEQniEHoWAAQmjAAloiQgAo2xPQknD1hPG3Id1AAQgPmxj5j7QjojrlgAAQmVAAkaDrg");
        this.shape_8.setTransform(4545.05, 1477.475);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#979796").s().p("EgP0Al1QkjiGjRj+QjQj7hulZQhvleAAmhQAAmoB5liQB1lZDcj7QDaj4EqiEQEviGFjABQIIAAFbEVQBtBYBOBnQAnA1ARAjIAPAAQgPh8AAh2IAAuKQAAidicABIklAAIAApQIKtAAQDwAABsBsQBsBsAADwMAAAA7yQAACcCcAAIElAAIAAJPIqXAAQjlAAhuhlQhnhgAAi7QAAhEAEhBIADg0IgOAAQgTApgqA9QhTB7h0BmQlzFIonAAQlpAAkriJgApZoUQi9BWiMClQkxFkAAJTQAAJHEdFkQCICpC6BbQC9BbDfAAQDPgBC0hJQC/hNCNiZQFAlaAAqHQAAj2g/jhQhCjph9ixQiGi9i6hlQjKhvj5AAQjUAAi7BXg");
        this.shape_9.setTransform(4169.225, 1410.5);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#979796").s().p("AV0c1QjwAAhshrQhshsABjwIAA6fQAAm1h7jOQickHmOAAQmbABk6D2QkyDyhtGLQhAC6AAEpIAAaZIqzAAMAAAgsnQAAididAAIkkAAIAApQIKXAAQHIAAAAGPIAACIQAABpgOBlIAOAAQA9iFBliAQB3iZCbh1QGKkoILAAQJ8AAEwFbQEsFUgBK4IAAYXQAACdCeAAIEjAAIAAJPg");
        this.shape_10.setTransform(3754.85, 1473.2);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#979796").s().p("Ar3bTQleiLkKj/QkMkCiSlWQiYlkAAmTQAAmRCYlhQCSlTEMkAQEKj+FeiKQFmiMGPAAQGNAAFnCMQFfCKEMD+QENEACTFTQCZFhAAGRQAAGSiZFkQiTFWkNECQkMEAlfCKQlnCOmNAAQmPAAlmiNgAnhyqQjeBeiqCsQirCvheDnQhhDyAAESQAAEUBhD0QBeDqCrCxQCqCuDeBeQDkBhD7AAQD9AADkhhQDgheCqiuQCsixBejqQBhjzAAkVQAAkThhjxQhejoisiuQiqisjgheQjkhfj9AAQj8AAjjBfg");
        this.shape_11.setTransform(3339.775, 1477.475);

        this.shape_12 = new cjs.Shape();
        this.shape_12.graphics.f("#979796").s().p("EgXUAn+MAAAhC5QAAidicAAIkkAAIAApQIKIAAQDpAABsBhQBkBZABCyQgBBsgGBUIANAAIA9hkQBUh4B1hkQF3lBIvAAQFnAAErCKQEiCGDTD9QDQD7BuFZQBxFfgBGhQAAGmh8FjQh3FZjfD6QjdD5kqCEQkvCFldAAQnyAAlhkgQhuhahThtIg8haIgOAAIAHBYQAHBvAABzIAAZDgAiq9WQjABOiMCZQlAFaAAKGQAAD1BBDjQBDDoB+CxQCGC9C5BmQDHBuDyABQDWgBC9hWQC9hXCOikQEylkAApUQAApHkdljQiIiqi6haQi9hbjfAAQjPAAi0BJg");
        this.shape_12.setTransform(2927.95, 1544.45);

        this.shape_13 = new cjs.Shape();
        this.shape_13.graphics.f("#979796").s().p("Av+YjQiWhjh4h3IhZhjIFknOIBNBUQBlBkB3BTQF7ELGdAAQEMAACrhyQC8h9AAjgQAAhzhUhiQhKhWiVhSQhyhAjJhTQjthgh7g0QnKjCjajFQkzkWAAmIQAAnvGKkPQFpj3JHAAQC3AAC5AgQDaAlCoBKQGtC8AAFvIAAFWIpsAAIAAiyQAAiSi8hXQiehJjEAAQk3AAivBrQi2BvAADRQAAB6BUBkQBKBYCVBRQByA+DIBQIFpCOQHLC5DZDCQEzEVAAGXQAADshlDHQhhDAi1CNQizCKjxBKQj1BKkZAAQpYAAnfk9g");
        this.shape_13.setTransform(2580.575, 1477.375);

        this.shape_14 = new cjs.Shape();
        this.shape_14.graphics.f("#979796").s().p("AohbQQlbiMkCkDQj/kAiJlXQiLldAAmNQAAmlCLllQCFlXD5j8QD0j4FKiFQFNiFF9AAQFyAAEsCEQEcB+DLDsQDCDkBmE1QBlExAAFhQAABcgWDAMgpEAAAQAPEZBqDoQBlDbCtCeQCoCZDaBRQDYBQDxAAQGEAAF0jSQC7hqBshpIErHsQiFCEjxCEQnhEHoWAAQmjAAlpiQgAo2xPQknD1hPG3Id1AAQgQmxj4j7QjojrlgAAQmVAAkaDrg");
        this.shape_14.setTransform(2252.075, 1477.475);

        this.shape_15 = new cjs.Shape();
        this.shape_15.graphics.f("#979796").s().p("EAaRAnTQidAAhegNQhsgPhLgmQiRhJh3juIqs1KQhfishjg0QhagvjWAAIqPAAIAAfSIrIAAMAAAhFAInQAAIAAplMAiSAAAQFKAAEWBlQETBkDIDAQDLDBBrEPQBvEXAAFTQAAI5k/F/QiBCaimBmQiTBbiRAgIAAAPQCNBSBJCSIJeS0QAxBnBbAjQBFAaCTAAIBdAAIAAJkgAtWhjIOdAAQGUAADmj1QDkjzAAmoQAAmkjijsQjhjqmNAAIurAAg");
        this.shape_15.setTransform(1881.525, 1406.225);

        this.shape_16 = new cjs.Shape();
        this.shape_16.graphics.f("#3770A7").s().p("EAa9BMgMgqkhHQQjwmRk6qjQhjjThejVIhLirIgbAAIA3JdQA2KpABGBMAAABHQMgldAAAMAAAiY/MAl4AAAMAqXBHeQDvGRE8KjQCdFRBtEBIAcAAQgbkIgclUQg3qqAAmAMAAAhHeMAldAAAMAAACY/g");
        this.shape_16.setTransform(4593.3, 575.75);

        this.shape_17 = new cjs.Shape();
        this.shape_17.graphics.f("#3770A7").s().p("EAUOBMgMgbKgzlIxjAAMAAAAzlMgldAAAMAAAiY/MA1TAAAQLdAAGxA3QGaAzFGB/QNQFEHYLVQHoLtAAQaQAAGLhcGIQheGRi2FgQi+FxkMEZQkdEsljC0IAAAbIBiCNQB/C/CREBMAdwA1ggA4fnKIQRAAQJFAAFJk2QFPk9AApCQAAkng9jJQhGjiiciRQlSk3tVAAIsoAAg");
        this.shape_17.setTransform(3733.475, 575.775);

        this.shape_18 = new cjs.Shape();
        this.shape_18.graphics.f("#3770A7").s().p("Eg5PBMgMAAAiY/MA4TAAAQLhAAJfCkQJnClG2FAQHCFJDrHYQD1HrgBJsQAAJ8kiIpQkzJHocE2IAAAbQMPDuGVKcQCwEhBYFcQBSFCAAFTQAAMFk0JIQkdIdocFqQnzFOqzCnQqCCcr3AAgEgTyAsYIWdABQHzAAEbk0QCAiLBCi8QBBi3AAjRQAAmzj8kOQkTkmn0gBI2rAAgAzzxOITRAAQGqAADnkWQDNj1AAl9QAAlvjTjcQjtj4nHAAIyoAAg");
        this.shape_18.setTransform(2879.45, 575.75);

        this.shape_19 = new cjs.Shape();
        this.shape_19.graphics.f("#3770A7").s().p("EgXcBJDQull0qxq2QqpqultucQlzusAAw2QAAwdGFupQF5uOK5qxQK8qyOgl3QO/mDQ8AAQNCAAL7DYQJiCtIaEvQGADYE1EEQCbCBBOBXIxVcPQhFg+h8heQj3i7kWicQt2n0tQAAQqmAAogDuQn6DdllGZQlRF/ivH/QioHqgBIeQABIpCyIGQC3IXFYGaQFtGzH1DtQIdEAKNAAQOcAAOkpbQEji8EBjiQCBhxBFhMITRbZQhXBnipCaQlUE0mgEBQpHFoqMDNQsxEBtxAAQxpAAvGmCg");
        this.shape_19.setTransform(2009.4, 575.775);

        this.shape_20 = new cjs.Shape();
        this.shape_20.graphics.f("#FFFFFF").s().p("AGhVsQgggTgJgjIjUsoQgIggAOgdQANgdAdgPQBDgfA3g3IACgCQA2g0AghEQAPgdAdgNQAdgNAfAJIMrDgQAjAMAQAhQAQAhgMAjQg2CbhZCKQhYCLh2ByIgCACQh0B1iLBXQiMBXicA0IgFACQgMADgMAAQgXAAgVgMgAvcPnQgkgCgYgcQlfmRADogQABiNAaiKIACgHQAJgjAhgSQAfgSAkAJIMnDfQAfAIASAbQASAagEAgQgBAMAAAaQgBAtAJAuQAQBRAxBGQATAagCAhQgDAggXAYIpWJPQgZAWggAAIgIgBgAizD8IgDgDQhWhXgDh6QgCh6BThbQAHgIAEgEIAAAAQBahZB9AAQB/AABaBZQBaBaABB+QABCAhaBbQhZBaiAABIgBAAQh+AAhahZgAEZk8Qg1gmg/gUQg/gThCAAQgTAAgSACQgfADgbgTQgZgSgJgfIjUsqIgBgHQgHgkAVgeQAVgeAkgHQCNgYCKABQIgADGOFlIAEADQAaAagBAlQAAAkgaAaIpSJLQgXAXggACIgGAAQgdAAgYgRg");
        this.shape_20.setTransform(521.4804, 811.3818);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_20 }, { t: this.shape_19 }, { t: this.shape_18 }, { t: this.shape_17 }, { t: this.shape_16 }, { t: this.shape_15 }, { t: this.shape_14 }, { t: this.shape_13 }, { t: this.shape_12 }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.instance_5 }, { t: this.instance_4 }, { t: this.instance_3 }, { t: this.instance_2 }, { t: this.instance_1 }, { t: this.shape_1 }, { t: this.shape }, { t: this.instance }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(0, 0, 5006.9, 1800.3);


    (lib.cbrn_logo = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.path125();
        this.instance.parent = this;
        this.instance.setTransform(-17.05, -32.55, 0.1166, 0.1166, 0, 0, 0, 146.3, 422);
        this.instance.alpha = 0.25;

        this.shape = new cjs.Shape();
        this.shape.graphics.f("#3770A7").s().p("EgppgPbIgZhEQjpoTAAp2QAAy5NXtcQNetXS2AAQS2AANfNXQNXNXAAS+QgBJ+jpILMgp9Bg0gEgKXg7pQk1CAj7D+Qn+H/AALOQAAKmG/HjQAeAhAhAeQH5H5LTAAQFnAAExh/QEvh+EAj8QAfgjAhgcQG+nrAAqeQAArMn5oBQn/n+rNAAQloAAk0CAg");
        this.shape.setTransform(-0.5318, -43.6108, 0.116, 0.116);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FFFFFF").s().p("AqiY/Qk3iEjwjwQjxjxiDk3QiJlCAAlhQAAlgCJlCQCDk3DxjwQDwjxE3iDQFCiJFgAAQFhAAFCCJQE4CDDwDxQDwDwCDE3QCJFCAAFgQAAFhiJFCQiDE3jwDxQjwDwk4CEQlCCIlhAAQlgAAlCiIg");
        this.shape_1.setTransform(-0.4912, -69.2351, 0.116, 0.116);

        this.instance_1 = new lib.polygon114();
        this.instance_1.parent = this;
        this.instance_1.setTransform(-21.5, -12.9, 0.1166, 0.1166, 0, 0, 0, 184, 254.3);
        this.instance_1.alpha = 0.25;

        this.instance_2 = new lib.svg4();
        this.instance_2.parent = this;
        this.instance_2.setTransform(-30.6, -9.05, 0.1167, 0.1167, 0, 0, 0, 142.7, 144.8);
        this.instance_2.alpha = 0.9492;

        this.instance_3 = new lib.g907();
        this.instance_3.parent = this;
        this.instance_3.setTransform(25.25, 39.75, 0.1167, 0.1167, 0, 0, 0, 136.3, 135.4);
        this.instance_3.alpha = 0.9492;

        this.instance_4 = new lib.g1399();
        this.instance_4.parent = this;
        this.instance_4.setTransform(26.75, -9.9, 0.1167, 0.1167, 0, 0, 0, 151.7, 150);
        this.instance_4.alpha = 0.9492;

        this.instance_5 = new lib.g833();
        this.instance_5.parent = this;
        this.instance_5.setTransform(-30.35, 39.5, 0.1167, 0.1167, 0, 0, 0, 180, 89.2);
        this.instance_5.alpha = 0.9492;

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FFFFFF").s().p("EgOGBxHQnAi2lZlZMhOYhOYQlZlZi2nAQivmxAAnWQAAnUCvmxQC2nBFZlZMBOYhOYQFRlSG4i3QG5i2HdABQHdgBG5C2QG5C3FRFSMBOXBOYQFaFZC2HBQCvGxAAHUQAAHWivGxQi2HAlaFZMhOXBOYIAAAAQlaFZnAC2QmxCvnVAAQnVAAmxivgEgJ9hmaQkyB/jqDrMhOYBOXQjvDwh/E3Qh6EtABFFQgBFGB6EtQB/E4DvDvMBOYBOYIABAAQDwDwE3B+QEsB6FFAAQFGAAEth6QE3h/DvjvMBOYhOYQDwjvB+k4QB7ktAAlGQAAlFh7ktQh+k3jwjwMhOYhOXQjpjrkzh/Qkyh+lLAAIgCAAQlKAAkxB+g");
        this.shape_2.setTransform(-0.4883, 16.0283, 0.116, 0.116);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#EF9700").s().p("Eg5lA5mMBzLhzKMAAABzKg");
        this.shape_3.setTransform(-42.9973, -26.2853, 0.1167, 0.1167);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#AC2B3A").s().p("Eg5kA5mMAAAhzKMBzKBzKg");
        this.shape_4.setTransform(43.0134, -26.2853, 0.1167, 0.1167);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#003461").s().p("Eg5lg5lMBzLAAAMAAABzLg");
        this.shape_5.setTransform(-42.9973, 59.7254, 0.1167, 0.1167);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#86BE39").s().p("Eg5kg5lMBzKAAAMhzKBzLg");
        this.shape_6.setTransform(43.0134, 59.7254, 0.1167, 0.1167);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FFFFFF").s().p("AGhVsQgggTgJgjIjUsoQgIggAOgdQANgdAdgPQBDgfA3g3IACgCQA2g0AghEQAPgdAdgNQAdgNAfAJIMrDgQAjAMAQAhQAQAhgMAjQg2CbhZCKQhYCLh2ByIgCACQh0B1iLBXQiMBXicA0IgFACQgMADgMAAQgXAAgVgMgAvcPnQgkgCgYgcQlfmRADogQABiNAaiKIACgHQAJgjAhgSQAfgSAkAJIMnDfQAfAIASAbQASAagEAgQgBAMAAAaQgBAtAJAuQAQBRAxBGQATAagCAhQgDAggXAYIpWJPQgZAWggAAIgIgBgAizD8IgDgDQhWhXgDh6QgCh6BThbQAHgIAEgEIAAAAQBahZB9AAQB/AABaBZQBaBaABB+QABCAhaBbQhZBaiAABIgBAAQh+AAhahZgAEZk8Qg1gmg/gUQg/gThCAAQgTAAgSACQgfADgbgTQgZgSgJgfIjUsqIgBgHQgHgkAVgeQAVgeAkgHQCNgYCKABQIgADGOFlIAEADQAaAagBAlQAAAkgaAaIpSJLQgXAXggACIgGAAQgdAAgYgRg");
        this.shape_7.setTransform(-25.6516, -8.5748, 0.1167, 0.1167);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.instance_5 }, { t: this.instance_4 }, { t: this.instance_3 }, { t: this.instance_2 }, { t: this.instance_1 }, { t: this.shape_1 }, { t: this.shape }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cbrn_logo, new cjs.Rectangle(-86.5, -103.2, 172.5, 206), null);


    (lib.br_text_box = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.brTextBox = new lib.brTextBox();
        this.brTextBox.name = "brTextBox";
        this.brTextBox.parent = this;
        this.brTextBox.setTransform(-161.5, -24.6, 0.9735, 1, 0, 0, 0, -161.5, -24.6);

        this.timeline.addTween(cjs.Tween.get(this.brTextBox).wait(1));

    }).prototype = getMCSymbolPrototype(lib.br_text_box, new cjs.Rectangle(-161.5, -24.6, 314.5, 49.2), null);


    (lib.br_text = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.brText = new lib.brText();
        this.brText.name = "brText";
        this.brText.parent = this;
        this.brText.setTransform(-61.35, -78.4, 1, 1, 0, 0, 0, -183.3, -272.5);

        this.timeline.addTween(cjs.Tween.get(this.brText).wait(1));

    }).prototype = getMCSymbolPrototype(lib.br_text, new cjs.Rectangle(-25.4, 13.6, 294.79999999999995, 361.09999999999997), null);


    (lib.br_logosvg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.pin = new lib.pin_1();
        this.pin.name = "pin";
        this.pin.parent = this;
        this.pin.setTransform(160.15, 260, 0.22, 0.22, 0, 180, 0);

        this.instance = new lib.ClipGroup_14();
        this.instance.parent = this;
        this.instance.setTransform(219.6, 210.5, 1, 1, 0, 0, 0, 219.6, 210.5);

        this.instance_1 = new lib.ClipGroup_1_1();
        this.instance_1.parent = this;
        this.instance_1.setTransform(219.6, 212.5, 1, 1, 0, 0, 0, 219.6, 210.5);

        this.instance_2 = new lib.ClipGroup_2_1();
        this.instance_2.parent = this;
        this.instance_2.setTransform(219.6, 212.5, 1, 1, 0, 0, 0, 219.6, 210.5);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.instance_2 }, { t: this.instance_1 }, { t: this.instance }, { t: this.pin }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(0, 0, 439.3, 423.1);


    (lib.br_full1svg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.pin = new lib.pin_1();
        this.pin.name = "pin";
        this.pin.parent = this;
        this.pin.setTransform(160.15, 260, 0.22, 0.22, 0, 180, 0);

        this.shape = new cjs.Shape();
        this.shape.graphics.f("#FF0000").s().p("AiqFpIAAoSQABgLgHgJQgGgJgMgBQgDgBgEABIg4AAIAAiXICiAAQAwAAAXAVQAYAVAAAtIAAApQAAAXgDAWIADAAQAZhPA6gyQBAg2BPAAQAOAAARADIAACvQgVgDgVAAQg/AAgzAjQg6AogXBKQgPA3AAA8IAAEag");
        this.shape.setTransform(1207.2, 183.275);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#FF0000").s().p("AAsF0QirAAhshuQhnhpAAidQAAikBlhoQBlhoCfAAQCUAABWBjQBSBcAACTQAAAVgFAsInqAAQAKBbA+AzQA6AxBTAAQBHAABIgoQAjgUAWgUIBKB7Qg8A0hKAcQhJAchNAAIgDgBgAhajFQguAmgPBEIE0AAQgCg/gngpQgmgog3AAQhDAAguAmg");
        this.shape_1.setTransform(1144.675, 183.7504);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#FF0000").s().p("Ak1GQQhXhmAAinQAAinBchnQBahlCPAAQBgAAA/ArQAgAWANAVIADAAQgCgSAAgYIAAh9QACgLgHgJQgHgJgLgCIg/AAIAAiXICoAAQAyAAAXAXQAWAXAAAxIAALLQgBAMAHAJQAIAJALABIA9AAIAACVIikAAQhYAAAAhIIAAgbIgDAAQgOAdgjAdQhHA6hlAAQiPAAhXhogAijgfQg0A8AABnQAABlAwA9QAxA9BPAAQBKAAAxg1QA2g9AAhvQAAhZgrg9QgxhEhTAAQhMAAgyA5g");
        this.shape_2.setTransform(1069.225, 170.625);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#FF0000").s().p("AD9FsQgyAAgXgWQgWgWAAgzIAAlGQAAhHgXgiQgZgohAAAQhEAAg0ApQgzAngTBBQgPAwABAuIAAFHIiwAAIAAoSQABgLgHgJQgGgJgMgBIgHAAIg4AAIAAiXICkAAQBeAAAABQIAAAVQAAAQgEAPIAEAAQAdg6A1gnQBIgzBgAAQD4AAAAEOIAAEVQgBAMAHAJQAIAJALABIA+AAIAACVg");
        this.shape_3.setTransform(986, 182.975);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#FF0000").s().p("AkUEMQhxhpAAijQAAihBxhrQBwhnCkAAQClAABwBoQBxBrAACgQAACihxBqQhwBpilAAQilAAhvhpgAiUifQg+BAAABfQAABhA+BAQA9A9BXABQBYAAA9g+QA/g/AAhiQAAhgg/g/Qg9g+hYABQhYAAg8A9g");
        this.shape_4.setTransform(903.025, 183.75);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#FF0000").s().p("Ak1H4IAAspQACgLgHgJQgHgJgLgBQgEgBgDABIg4AAIAAiXICdAAQBaAAgBBEIAAAcIADAAIANgRQAQgWAWgRQBIg5BnAAQCQAABWBoQBXBmAACnQAACmheBoQhbBmiOgBQhXABhDgvQgVgPgQgSIgMgOIgCAAQACAZAAAlIAAEmgAhSkpQg3A8AABvQAABaAsA8QAxBEBSAAQBMAAAzg6QA1g7AAhmQAAhlgyg+Qgwg9hQAAQhKABgwA1g");
        this.shape_5.setTransform(821, 196.9);

        this.shape_6 = new cjs.Shape();
        this.shape_6.graphics.f("#FF0000").s().p("AjRE1QgfgUgZgXIgTgUIBUh0IA8A0QBMA1BLAAQArAAAagRQAbgTAAgiQAAggg6geQhSgigugUQhXgmgrgoQg6g5AAhOQAAhlBTg4QBKgzB2AAQBdAABEAfQBZAnAABOIAABKIicAAIAAgjQAAgXgegOQgagNgiAAQgvAAgcARQgeARAAAeQAAAkA7AdQAjASBfAiQBXAjArAnQA7A3AABQQAABjhPA8QhMA7h3AAQh5AAhjhAg");
        this.shape_6.setTransform(750.675, 183.775);

        this.shape_7 = new cjs.Shape();
        this.shape_7.graphics.f("#FF0000").s().p("AAsF0QirAAhrhuQhohpAAidQABikBkhoQBlhoCfAAQCUAABWBjQBSBcAACTQAAAVgFAsInqAAQAKBbA9AzQA7AxBSAAQBIAABHgoQAkgUAVgUIBLB7Qg8A0hKAcQhJAchNAAIgDgBgAhajFQguAmgPBEIE0AAQgCg/gngpQgmgog3AAQhDAAguAmg");
        this.shape_7.setTransform(684.85, 183.7504);

        this.shape_8 = new cjs.Shape();
        this.shape_8.graphics.f("#FF0000").s().p("AE6HvQg6AAgbgNQgegPgXguIh/j6QgPgcgVgJQgRgJgnAAIhdAAIAAFyIi2AAIAAtDIhbAAIAAibIG6AAQCKAABVBQQBVBRAACKQAAB1hCBOQgxA4gxAJIAAADQAhASAQAgIBgC/QALAVATAGQAOAGAagBIASAAIAACbgAiIgdICQAAQBGgBAmgpQAngpAAhKQAAhKgngoQglgohEAAIiTAAg");
        this.shape_8.setTransform(610.65, 169.8);

        this.shape_9 = new cjs.Shape();
        this.shape_9.graphics.f("#3770A7").s().p("AkUEMQhxhpAAijQAAihBxhrQBwhnCkAAQCkAABwBoQByBrAACgQAACihyBqQhwBpikAAQimAAhuhpgAiVifQg+BAAABfQAABhA+BAQA+A+BXAAIAAgBQBYAAA9g9QA+g/AAhiQAAhgg+hAQg9g8hYAAQhZgBg8A+g");
        this.shape_9.setTransform(527, 183.75);

        this.shape_10 = new cjs.Shape();
        this.shape_10.graphics.f("#3770A7").s().p("AAGHvQgxAAgWgVQgWgXAAgyIAAmzQABgLgGgJQgIgJgKgBIgIAAIg3AAIAAiXICpAAQAvAAAXAXQAWAWAAAyIAAGzQgBAMAHAKQAHAIAMACIA+AAIAACUgAhPlLIAAikICZAAIAACkg");
        this.shape_10.setTransform(468.25, 169.8);

        this.shape_11 = new cjs.Shape();
        this.shape_11.graphics.f("#3770A7").s().p("AjJHvQgxAAgXgVQgXgXABgyIgBrlIhaAAIAAibIGrAAQCJAABUBDQBWBFAAB4QAABNgmA7QggAzgyAZIAAADQBNAXArBDQAoA+AABQQgBCNhlBNQhcBFiUAAgAhSFVICNAAQBCAAAmgoQAkgoAAhAQAAhAglgmQglgohCgBIisAAIAAEAQgBALAIAKQAHAIAMACIAFgBgAhxhaICcAAQA3ABAhglQAfgkABg2QAAg3gggiQgggig8gBIiYAAg");
        this.shape_11.setTransform(408.45, 169.8);

        this.instance = new lib.ClipGroup_14();
        this.instance.parent = this;
        this.instance.setTransform(219.6, 210.5, 1, 1, 0, 0, 0, 219.6, 210.5);

        this.instance_1 = new lib.ClipGroup_1_1();
        this.instance_1.parent = this;
        this.instance_1.setTransform(219.6, 212.5, 1, 1, 0, 0, 0, 219.6, 210.5);

        this.instance_2 = new lib.ClipGroup_2_1();
        this.instance_2.parent = this;
        this.instance_2.setTransform(219.6, 212.5, 1, 1, 0, 0, 0, 219.6, 210.5);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.instance_2 }, { t: this.instance_1 }, { t: this.instance }, { t: this.shape_11 }, { t: this.shape_10 }, { t: this.shape_9 }, { t: this.shape_8 }, { t: this.shape_7 }, { t: this.shape_6 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }, { t: this.pin }] }).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(0, 0, 1233, 423.1);


    (lib.rrDrop = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.ClipGroup_11();
        this.instance.parent = this;
        this.instance.setTransform(9.65, -7.05, 0.3749, 0.3749, 0, 0, 0, 192.1, 176.6);

        this.white_circle_rr = new lib.white_circle();
        this.white_circle_rr.name = "white_circle_rr";
        this.white_circle_rr.parent = this;

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.white_circle_rr }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rrDrop, new cjs.Rectangle(-107, -93, 218, 218), null);


    (lib.crDrop = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.chem_svg();
        this.instance.parent = this;
        this.instance.setTransform(14.85, 21.8, 0.3513, 0.3513, 0, 0, 0, 139.1, 190.3);

        this.white_circle_rr = new lib.white_circle();
        this.white_circle_rr.name = "white_circle_rr";
        this.white_circle_rr.parent = this;

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.white_circle_rr }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.crDrop, new cjs.Rectangle(-107, -93, 218, 218), null);


    (lib.cbrnPill = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.CBRNResponder_logo_19Jun2019_1svg("synched", 0);
        this.instance.parent = this;
        this.instance.setTransform(-278.35, -94.3, 0.1167, 0.1167, 0, 0, 0, 292.2, 106.3);

        this.instance_1 = new lib.cbrn_shape1();
        this.instance_1.parent = this;
        this.instance_1.setTransform(0.05, -0.05, 1, 1, 0, 0, 0, 474.9, 219.3);
        this.instance_1.shadow = new cjs.Shadow("rgba(204,204,204,1)", 0, 5, 0);

        this.instance_2 = new lib.cbrn_shape1();
        this.instance_2.parent = this;
        this.instance_2.setTransform(0.05, -0.05, 1, 1, 0, 0, 0, 474.9, 219.3);
        this.instance_2.shadow = new cjs.Shadow("rgba(0,0,0,0.349)", 0, 21, 62);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.instance_2 }, { t: this.instance_1 }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cbrnPill, new cjs.Rectangle(-478.5, -223.5, 960, 490), null);


    (lib.brDrop = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.br_logosvg("synched", 0);
        this.instance.parent = this;
        this.instance.setTransform(0, -4.15, 0.3491, 0.3491, 0, 0, 0, 230.6, 175.3);

        this.pin = new lib.pin();
        this.pin.name = "pin";
        this.pin.parent = this;
        this.pin.setTransform(-25.2, 25.95, 0.0777, 0.0777, 0, 180, 0, -2.6, -1.3);

        this.white_circle_rr = new lib.white_circle();
        this.white_circle_rr.name = "white_circle_rr";
        this.white_circle_rr.parent = this;

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.white_circle_rr }, { t: this.pin }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.brDrop, new cjs.Rectangle(-107, -93, 218, 218), null);


    (lib.backButton = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#000000").s().p("AAECIIgUhPIgIAAIgPBPIgxAAIA2kPIAwAAIgeCZIAIAAIAphGIA4AAIg+BUIAhBog");
        this.shape.setTransform(120.575, 1.475);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#000000").s().p("Ag8BKQgQgYAGgmIAEgXQAGgnAWgXQAXgYAiAAQAcAAAPAUQAOAUgFAiIguAAQADgQgDgJQgDgJgJAAQgNAAgHANQgHAMgDAVIgEAXQgEAXADAMQADALAOAAQAIAAAGgHQAGgIACgOIAsAAIABABQgFAfgUASQgVATgbAAQgiAAgPgYg");
        this.shape_1.setTransform(104.3573, 5.625);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#000000").s().p("AhDBSQgLgPAFgZQAGgeATgNQAUgPAhAAIARAAIADgPQADgPgDgHQgDgGgJAAQgHAAgFAGQgEAGgCALIgvgBIgBgBQAFgaAVgQQAVgRAeAAQAaAAAPASQAPARgGAfIgPBMQgDAOAAAMQgBALACAMIgxAAIgBgKIgBgLQgIALgKAHQgLAHgMAAQgVAAgLgQgAgPAUQgGAIgCALQgCALACAFQADAGAHAAQAGAAAGgEQAGgEAEgGIAHgjIgQAAQgJAAgGAIg");
        this.shape_2.setTransform(88.385, 5.625);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#000000").s().p("AgRCEQgJgGgGgKIgGASIgrAAIA2kOIAxAAIgUBeQAIgHAJgEQAIgEAJAAQAcAAALAeQAMAcgJArIgBAEQgJAtgSAXQgSAVgdAAQgLAAgJgFgAAFgMQgFAFgFAIIgSBZQACAEAFACQAFACAHAAQAMAAAIgNQAHgLAFgaIABgEQAGgegBgPQgCgRgOABQgGgBgHAGg");
        this.shape_3.setTransform(71.9726, 1.65);

        this.instance = new lib.cbrn_logo();
        this.instance.parent = this;
        this.instance.setTransform(181.75, -0.05, 0.4565, 0.4565);

        this.instance_1 = new lib.white_tab();
        this.instance_1.parent = this;
        this.instance_1.setTransform(181.75, -0.05);

        this.back_button = new lib.cbrn_tab_button();
        this.back_button.name = "back_button";
        this.back_button.parent = this;
        new cjs.ButtonHelper(this.back_button, 0, 1, 2, false, new lib.cbrn_tab_button(), 3);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f().s("#000000").ss(6.6, 2, 0, 3).p("ABBCEIiCiEICDiC");
        this.shape_4.setTransform(41.6272, 2.6);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_4 }, { t: this.back_button }, { t: this.instance_1 }, { t: this.instance }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.backButton, new cjs.Rectangle(-267.8, -93, 559.6, 217), null);


    (lib._rrLogoSlide = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { "open": 1, "opened": 9, "close": 10 });

        // timeline functions:
        this.frame_0 = function () {
            this.stop();
        }
        this.frame_9 = function () {
            this.stop();
        }
        this.frame_18 = function () {
            this.stop();
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(9).call(this.frame_9).wait(9).call(this.frame_18).wait(1));

        // shape (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        var mask_graphics_0 = new cjs.Graphics().p("AlQO4QBWg/BPhPQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmiGiQgZAagbAYg");
        var mask_graphics_1 = new cjs.Graphics().p("AlQO4QBWg/BPhPQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmiGiQgZAagbAYg");
        var mask_graphics_2 = new cjs.Graphics().p("AuTO4QBWg/BPhPQF9l8AAoaQAAnmk2llIUpAAQESFtAAHfQAAJPmiGiIg1Ayg");
        var mask_graphics_3 = new cjs.Graphics().p("A3WO4QBWg/BPhPQF9l8AAoaQAAnmk2llMAmvAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_4 = new cjs.Graphics().p("EggZAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMA41AAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_5 = new cjs.Graphics().p("EgpcAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBK7AAAQESFtAAHfQAAJPmiGiIg1Ayg");
        var mask_graphics_6 = new cjs.Graphics().p("EgyfAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBdBAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_7 = new cjs.Graphics().p("Eg7iAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBvHAAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_8 = new cjs.Graphics().p("EhElAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCBNAAAQESFtAAHfQAAJPmiGiIg1Ayg");
        var mask_graphics_9 = new cjs.Graphics().p("EhNoAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCTTAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_10 = new cjs.Graphics().p("EhNoAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCTTAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_11 = new cjs.Graphics().p("EhElAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCBNAAAQESFtAAHfQAAJPmiGiIg1Ayg");
        var mask_graphics_12 = new cjs.Graphics().p("Eg7iAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBvHAAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_13 = new cjs.Graphics().p("EgyfAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBdBAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_14 = new cjs.Graphics().p("EgpcAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBK7AAAQESFtAAHfQAAJPmiGiIg1Ayg");
        var mask_graphics_15 = new cjs.Graphics().p("EggZAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMA41AAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_16 = new cjs.Graphics().p("A3WO4QBWg/BPhPQF9l8AAoaQAAnmk2llMAmvAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_17 = new cjs.Graphics().p("AuTO4QBWg/BPhPQF9l8AAoaQAAnmk2llIUpAAQESFtAAHfQAAJPmiGiIg1Ayg");
        var mask_graphics_18 = new cjs.Graphics().p("AlQO4QBWg/BPhPQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmiGiQgZAagbAYg");

        this.timeline.addTween(cjs.Tween.get(mask).to({ graphics: mask_graphics_0, x: -361.1, y: 10.475 }).wait(1).to({ graphics: mask_graphics_1, x: -361.1, y: 10.475 }).wait(1).to({ graphics: mask_graphics_2, x: -303.2, y: 10.475 }).wait(1).to({ graphics: mask_graphics_3, x: -245.3, y: 10.475 }).wait(1).to({ graphics: mask_graphics_4, x: -187.4, y: 10.475 }).wait(1).to({ graphics: mask_graphics_5, x: -129.5, y: 10.475 }).wait(1).to({ graphics: mask_graphics_6, x: -71.6, y: 10.475 }).wait(1).to({ graphics: mask_graphics_7, x: -13.7, y: 10.475 }).wait(1).to({ graphics: mask_graphics_8, x: 44.2, y: 10.475 }).wait(1).to({ graphics: mask_graphics_9, x: 102.1, y: 10.475 }).wait(1).to({ graphics: mask_graphics_10, x: 102.1, y: 10.475 }).wait(1).to({ graphics: mask_graphics_11, x: 44.2, y: 10.475 }).wait(1).to({ graphics: mask_graphics_12, x: -13.7, y: 10.475 }).wait(1).to({ graphics: mask_graphics_13, x: -71.6, y: 10.475 }).wait(1).to({ graphics: mask_graphics_14, x: -129.5, y: 10.475 }).wait(1).to({ graphics: mask_graphics_15, x: -187.4, y: 10.475 }).wait(1).to({ graphics: mask_graphics_16, x: -245.3, y: 10.475 }).wait(1).to({ graphics: mask_graphics_17, x: -303.2, y: 10.475 }).wait(1).to({ graphics: mask_graphics_18, x: -361.1, y: 10.475 }).wait(1));

        // rr_svg_svg
        this.instance = new lib.rr_svgsvg("synched", 0);
        this.instance.parent = this;
        this.instance.setTransform(-956.1, -19.55, 1, 1, 0, 0, 0, 605.8, 176.6);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).to({ startPosition: 0 }, 1).to({ x: -29.7 }, 8).to({ startPosition: 0 }, 1).to({ x: -956.1 }, 8).wait(1));

        // shape (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        var mask_1_graphics_0 = new cjs.Graphics().p("At+N/QlylzAAoMQAAoLFylzQFzlzILABQIMgBFyFzQFzFzAAILQAAIMlzFzQlyFyoMAAQoLAAlzlyg");
        var mask_1_graphics_1 = new cjs.Graphics().p("At+N/QlylzAAoMQAAoLFylzQFzlzILABQIMgBFyFzQFzFzAAILQAAIMlzFzQlyFyoMAAQoLAAlzlyg");

        this.timeline.addTween(cjs.Tween.get(mask_1).to({ graphics: mask_1_graphics_0, x: -471.975, y: 0 }).wait(1).to({ graphics: mask_1_graphics_1, x: -471.975, y: 0 }).wait(18));

        // rr_svg_svg
        this.instance_1 = new lib.rr_svgsvg("synched", 0);
        this.instance_1.parent = this;
        this.instance_1.setTransform(-29.7, -19.55, 1, 1, 0, 0, 0, 605.8, 176.6);

        var maskedShapeInstanceList = [this.instance_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_1).wait(1).to({ startPosition: 0 }, 0).wait(18));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-576.1, -112, 1152.3000000000002, 224.1);


    (lib._crLogoSlide = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { "open": 1, "opened": 9, "close": 10 });

        // timeline functions:
        this.frame_0 = function () {
            this.stop();
        }
        this.frame_9 = function () {
            this.stop();
        }
        this.frame_18 = function () {
            this.stop();
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(9).call(this.frame_9).wait(9).call(this.frame_18).wait(1));

        // shape (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        var mask_graphics_0 = new cjs.Graphics().p("AIEUaQhSgSisgvQibgmhrAAQhpAAibAiIj6A3QiKAYhegQQh1gThWhWQilimhXmoQhDlJAAlRQAAoMFylzQFzlzILAAQIMAAFyFzQFzFzAAIMQAAFeg+FKQhTG4ilCkQhVBWh3AQQgbADgeAAQhNAAhlgWg");
        var mask_graphics_1 = new cjs.Graphics().p("AIEUaQhSgSisgvQibgmhrAAQhpAAibAiIj6A3QiKAYhegQQh1gThWhWQilimhXmoQhDlJAAlRQAAoMFylzQFzlzILAAQIMAAFyFzQFzFzAAIMQAAFeg+FKQhTG4ilCkQhVBWh3AQQgbADgeAAQhNAAhlgWg");
        var mask_graphics_10 = new cjs.Graphics().p("AIEUaQhSgSisgvQibgmhrAAQhpAAibAiIj6A3QiKAYhegQQh1gThWhWQilimhXmoQhDlJAAlRQAAoMFylzQFzlzILAAQIMAAFyFzQFzFzAAIMQAAFeg+FKQhTG4ilCkQhVBWh3AQQgbADgeAAQhNAAhlgWg");

        this.timeline.addTween(cjs.Tween.get(mask).to({ graphics: mask_graphics_0, x: -471.975, y: 6.2444 }).wait(1).to({ graphics: mask_graphics_1, x: -471.975, y: 6.2444 }).wait(9).to({ graphics: mask_graphics_10, x: -471.975, y: 6.2444 }).wait(9));

        // rr_svg_svg
        this.instance = new lib.ChemResponder_logo__blueorangesvgcopy("synched", 0);
        this.instance.parent = this;
        this.instance.setTransform(-15.9, 54.05, 1, 1, 0, 0, 0, 605.8, 176.6);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).wait(1).to({ startPosition: 0 }, 0).wait(9).to({ startPosition: 0 }, 0).wait(9));

        // shape (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        var mask_1_graphics_0 = new cjs.Graphics().p("AlQO4QBWg+BPhQQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmiGiQgcAdgYAVg");
        var mask_1_graphics_1 = new cjs.Graphics().p("AlQO4QBWg+BPhQQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmiGiQgcAdgYAVg");
        var mask_1_graphics_2 = new cjs.Graphics().p("AvkO4QBWg+BPhQQF9l8AAoaQAAnmk2llIXLAAQESFtAAHfQAAJPmjGiQgcAdgYAVg");
        var mask_1_graphics_3 = new cjs.Graphics().p("A53O4QBVg+BQhQQF9l8AAoaQAAnmk2llMArxAAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_4 = new cjs.Graphics().p("EgkLAO4QBWg+BPhQQF9l8AAoaQAAnmk2llMBAZAAAQESFtAAHfQAAJPmjGiQgcAdgYAVg");
        var mask_1_graphics_5 = new cjs.Graphics().p("EgufAO4QBWg+BPhQQF+l8AAoaQgBnmk2llMBVBAAAQERFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_6 = new cjs.Graphics().p("Eg4yAO4QBVg+BQhQQF9l8AAoaQAAnmk2llMBpnAAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_7 = new cjs.Graphics().p("EhDGAO4QBWg+BPhQQF9l8AAoaQAAnmk2llMB+PAAAQESFtAAHfQAAJPmjGiQgcAdgYAVg");
        var mask_1_graphics_8 = new cjs.Graphics().p("EhNZAO4QBVg+BQhQQF9l8AAoaQAAnmk2llMCS1AAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_9 = new cjs.Graphics().p("EhXtAO4QBWg+BPhQQF9l8AAoaQAAnmk2llMCndAAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_10 = new cjs.Graphics().p("EhXtAO4QBWg+BPhQQF9l8AAoaQAAnmk2llMCndAAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_11 = new cjs.Graphics().p("EhNZAO4QBVg+BQhQQF9l8AAoaQAAnmk2llMCS1AAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_12 = new cjs.Graphics().p("EhDGAO4QBWg+BPhQQF9l8AAoaQAAnmk2llMB+PAAAQESFtAAHfQAAJPmjGiQgcAdgYAVg");
        var mask_1_graphics_13 = new cjs.Graphics().p("Eg4yAO4QBVg+BQhQQF9l8AAoaQAAnmk2llMBpnAAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_14 = new cjs.Graphics().p("EgufAO4QBWg+BPhQQF+l8AAoaQgBnmk2llMBVBAAAQERFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_15 = new cjs.Graphics().p("EgkLAO4QBWg+BPhQQF9l8AAoaQAAnmk2llMBAZAAAQESFtAAHfQAAJPmjGiQgcAdgYAVg");
        var mask_1_graphics_16 = new cjs.Graphics().p("A53O4QBVg+BQhQQF9l8AAoaQAAnmk2llMArxAAAQESFtAAHfQAAJPmiGiQgdAdgXAVg");
        var mask_1_graphics_17 = new cjs.Graphics().p("AvkO4QBWg+BPhQQF9l8AAoaQAAnmk2llIXLAAQESFtAAHfQAAJPmjGiQgcAdgYAVg");
        var mask_1_graphics_18 = new cjs.Graphics().p("AlQO4QBWg+BPhQQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmiGiQgcAdgYAVg");

        this.timeline.addTween(cjs.Tween.get(mask_1).to({ graphics: mask_1_graphics_0, x: -380.2, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_1, x: -380.2, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_2, x: -314.2375, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_3, x: -248.275, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_4, x: -182.3125, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_5, x: -116.35, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_6, x: -50.3875, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_7, x: 15.575, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_8, x: 81.5375, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_9, x: 147.5, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_10, x: 147.5, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_11, x: 81.5375, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_12, x: 15.575, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_13, x: -50.3875, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_14, x: -116.35, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_15, x: -182.3125, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_16, x: -248.275, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_17, x: -314.2375, y: 10.475 }).wait(1).to({ graphics: mask_1_graphics_18, x: -380.2, y: 10.475 }).wait(1));

        // rr_svg_svg
        this.instance_1 = new lib.ChemResponder_logo__blueorangesvgcopy("synched", 0);
        this.instance_1.parent = this;
        this.instance_1.setTransform(-1093.95, 54.05, 1, 1, 0, 0, 0, 605.8, 176.6);

        var maskedShapeInstanceList = [this.instance_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_1).to({ startPosition: 0 }, 1).to({ x: -15.9 }, 8).wait(1).to({ startPosition: 0 }, 0).to({ x: -1093.95 }, 8).wait(1));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-566.5, -121.7, 1262.7, 247.4);


    (lib._brLogoSlide = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { "open": 1, "opened": 9, "close": 10 });

        // timeline functions:
        this.frame_0 = function () {
            this.stop();
        }
        this.frame_9 = function () {
            this.stop();
        }
        this.frame_18 = function () {
            this.stop();
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(9).call(this.frame_9).wait(9).call(this.frame_18).wait(1));

        // shape (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        var mask_graphics_0 = new cjs.Graphics().p("AlQO4QBWg/BPhPQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmhGiIg0Ayg");
        var mask_graphics_1 = new cjs.Graphics().p("AlQO4QBWg/BPhPQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmhGiIg0Ayg");
        var mask_graphics_2 = new cjs.Graphics().p("AuTO4QBWg/BPhPQF9l8AAoaQAAnmk2llIUpAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_3 = new cjs.Graphics().p("A3WO4QBWg/BPhPQF9l8AAoaQAAnmk2llMAmvAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_4 = new cjs.Graphics().p("EggZAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMA41AAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_5 = new cjs.Graphics().p("EgpcAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBK7AAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_6 = new cjs.Graphics().p("EgyfAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBdBAAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_7 = new cjs.Graphics().p("Eg7iAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBvHAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_8 = new cjs.Graphics().p("EhElAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCBNAAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_9 = new cjs.Graphics().p("EhNoAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCTTAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_10 = new cjs.Graphics().p("EhNoAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCTTAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_11 = new cjs.Graphics().p("EhElAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMCBNAAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_12 = new cjs.Graphics().p("Eg7iAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBvHAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_13 = new cjs.Graphics().p("EgyfAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBdBAAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_14 = new cjs.Graphics().p("EgpcAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMBK7AAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_15 = new cjs.Graphics().p("EggZAO4QBWg/BPhPQF9l8AAoaQAAnmk2llMA41AAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_16 = new cjs.Graphics().p("A3WO4QBWg/BPhPQF9l8AAoaQAAnmk2llMAmvAAAQESFtAAHfQAAJPmiGiIg0Ayg");
        var mask_graphics_17 = new cjs.Graphics().p("AuTO4QBWg/BPhPQF9l8AAoaQAAnmk2llIUpAAQESFtAAHfQAAJPmjGiIg0Ayg");
        var mask_graphics_18 = new cjs.Graphics().p("AlQO4QBWg/BPhPQF8l8AAoaQAAnmk1llICjAAQESFtAAHfQAAJPmhGiIg0Ayg");

        this.timeline.addTween(cjs.Tween.get(mask).to({ graphics: mask_graphics_0, x: -342.35, y: 10.475 }).wait(1).to({ graphics: mask_graphics_1, x: -342.35, y: 10.475 }).wait(1).to({ graphics: mask_graphics_2, x: -286.7937, y: 10.475 }).wait(1).to({ graphics: mask_graphics_3, x: -231.2375, y: 10.475 }).wait(1).to({ graphics: mask_graphics_4, x: -175.6812, y: 10.475 }).wait(1).to({ graphics: mask_graphics_5, x: -120.125, y: 10.475 }).wait(1).to({ graphics: mask_graphics_6, x: -64.5687, y: 10.475 }).wait(1).to({ graphics: mask_graphics_7, x: -9.0125, y: 10.475 }).wait(1).to({ graphics: mask_graphics_8, x: 46.5438, y: 10.475 }).wait(1).to({ graphics: mask_graphics_9, x: 102.1, y: 10.475 }).wait(1).to({ graphics: mask_graphics_10, x: 102.1, y: 10.475 }).wait(1).to({ graphics: mask_graphics_11, x: 46.5438, y: 10.475 }).wait(1).to({ graphics: mask_graphics_12, x: -9.0125, y: 10.475 }).wait(1).to({ graphics: mask_graphics_13, x: -64.5687, y: 10.475 }).wait(1).to({ graphics: mask_graphics_14, x: -120.125, y: 10.475 }).wait(1).to({ graphics: mask_graphics_15, x: -175.6812, y: 10.475 }).wait(1).to({ graphics: mask_graphics_16, x: -231.2375, y: 10.475 }).wait(1).to({ graphics: mask_graphics_17, x: -286.7937, y: 10.475 }).wait(1).to({ graphics: mask_graphics_18, x: -342.35, y: 10.475 }).wait(1));

        // rr_svg_svg
        this.instance = new lib.br_full1svg("synched", 0);
        this.instance.parent = this;
        this.instance.setTransform(-973.2, -9.8, 1, 1, 0, 0, 0, 605.8, 176.6);

        var maskedShapeInstanceList = [this.instance];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance).to({ startPosition: 0 }, 1).to({ x: -94.45, y: -4.25 }, 8).wait(1).to({ startPosition: 0 }, 0).to({ x: -973.2, y: -9.8 }, 8).wait(1));

        // shape (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        var mask_1_graphics_0 = new cjs.Graphics().p("EgpbAPjQmcmcAApHQAApGGcmdQGdmcJHAAQJHAAGcGcQGdGdAAJGQAAJHmdGcQmcGdpHAAQpHAAmdmdg");
        var mask_1_graphics_1 = new cjs.Graphics().p("EgpbAPjQmcmcAApHQAApGGcmdQGdmcJHAAQJHAAGcGcQGdGdAAJGQAAJHmdGcQmcGdpHAAQpHAAmdmdg");
        var mask_1_graphics_10 = new cjs.Graphics().p("EgpbAPjQmcmcAApHQAApGGcmdQGdmcJHAAQJHAAGcGcQGdGdAAJGQAAJHmdGcQmcGdpHAAQpHAAmdmdg");

        this.timeline.addTween(cjs.Tween.get(mask_1).to({ graphics: mask_1_graphics_0, x: -306.3735, y: 0 }).wait(1).to({ graphics: mask_1_graphics_1, x: -306.3735, y: 0 }).wait(9).to({ graphics: mask_1_graphics_10, x: -306.3735, y: 0 }).wait(9));

        // rr_svg_svg
        this.instance_1 = new lib.br_full1svg("synched", 0);
        this.instance_1.parent = this;
        this.instance_1.setTransform(-94.45, -4.25, 1, 1, 0, 0, 0, 605.8, 176.6);

        var maskedShapeInstanceList = [this.instance_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_1).wait(1).to({ startPosition: 0 }, 0).wait(9).to({ startPosition: 0 }, 0).wait(9));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(-590.9, -120.3, 1123.6, 229.6);


    (lib.rr_circle = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.rrDrop = new lib.rrDrop();
        this.rrDrop.name = "rrDrop";
        this.rrDrop.parent = this;
        this.rrDrop.setTransform(9.65, -7, 1, 1, 0, 0, 0, 9.7, -6.2);

        this.timeline.addTween(cjs.Tween.get(this.rrDrop).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rr_circle, new cjs.Rectangle(-107, -93.8, 217, 217), null);


    (lib.imaac_svg = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2 (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        mask.graphics.p("EgvzA94MAAAh7vMBfnAAAMAAAB7vg");
        mask.setTransform(306, 396);

        // Layer_3
        this.instance = new lib.ClipGroup_16();
        this.instance.parent = this;
        this.instance.setTransform(306, 396, 1, 1, 0, 0, 0, 306, 396);

        this.shape = new cjs.Shape();
        this.shape.graphics.f("#1C375F").s().p("AgOAPIgdgMIAbgPIACghIAXAWIAfgIIgNAdIARAaIgfgDIgUAYg");
        this.shape.setTransform(55.525, 182.95);

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f("#1C375F").s().p("AAAAcIgcAPIAGggIgXgVIAggEIAOgdIANAdIAgAGIgYAUIAFAgg");
        this.shape_1.setTransform(62.175, 128.75);

        this.shape_2 = new cjs.Shape();
        this.shape_2.graphics.f("#1C375F").s().p("AgLAVIggAEIARgaIgNgeIAfAIIAXgVIACAfIAbAPIgdANIgHAeg");
        this.shape_2.setTransform(84.025, 92.2);

        this.shape_3 = new cjs.Shape();
        this.shape_3.graphics.f("#1C375F").s().p("AgNAPIgfgJIAZgSIAAggIAZASIAdgKIgKAeIAUAYIgfAAIgRAbg");
        this.shape_3.setTransform(119.4, 66.2);

        this.shape_4 = new cjs.Shape();
        this.shape_4.graphics.f("#1C375F").s().p("AgTAMIgZgTIAegIIAKgbIADAAIAOAYIAhACIgVAXIAJAfIgegMIgaARg");
        this.shape_4.setTransform(157.8, 56.175);

        this.shape_5 = new cjs.Shape();
        this.shape_5.graphics.f("#1C375F").s().p("AgLAWIggAFIAQgbIgOgdIAfAGIAWgVIAEAgIAcANIgdAOIgGAeg");
        this.shape_5.setTransform(200.025, 61.25);

        this.instance_1 = new lib.ClipGroup_1_0();
        this.instance_1.parent = this;
        this.instance_1.setTransform(306, 396, 1, 1, 0, 0, 0, 306, 396);

        this.instance_2 = new lib.ClipGroup_2_0();
        this.instance_2.parent = this;
        this.instance_2.setTransform(241.5, 164.6, 1, 1, 0, 0, 0, 41.7, 101.3);

        this.instance_3 = new lib.ClipGroup_3_0();
        this.instance_3.parent = this;
        this.instance_3.setTransform(233.3, 165, 1, 1, 0, 0, 0, 36, 96.7);

        this.instance_4 = new lib.ClipGroup_4();
        this.instance_4.parent = this;
        this.instance_4.setTransform(212.9, 164.6, 1, 1, 0, 0, 0, 70.3, 101.3);

        this.instance_5 = new lib.ClipGroup_5();
        this.instance_5.parent = this;
        this.instance_5.setTransform(198.55, 165, 1, 1, 0, 0, 0, 70.8, 96.7);

        this.instance_6 = new lib.ClipGroup_6();
        this.instance_6.parent = this;
        this.instance_6.setTransform(166.1, 186.8, 1, 1, 0, 0, 0, 103.3, 74.8);

        this.instance_7 = new lib.ClipGroup_7();
        this.instance_7.parent = this;
        this.instance_7.setTransform(197.65, 185.2, 1, 1, 0, 0, 0, 69.8, 73.2);

        this.instance_8 = new lib.ClipGroup_8();
        this.instance_8.parent = this;
        this.instance_8.setTransform(154.4, 185.2, 1, 1, 0, 0, 0, 91.6, 73.2);

        this.instance_9 = new lib.ClipGroup_9();
        this.instance_9.parent = this;
        this.instance_9.setTransform(190.25, 164.45, 1, 1, 0, 0, 0, 62.4, 96.4);

        this.instance_10 = new lib.ClipGroup_10();
        this.instance_10.parent = this;
        this.instance_10.setTransform(306, 396, 1, 1, 0, 0, 0, 306, 396);

        this.instance_11 = new lib.ClipGroup_11_1();
        this.instance_11.parent = this;
        this.instance_11.setTransform(208.75, 114, 1, 1, 0, 0, 0, 12.8, 11.7);

        this.instance_12 = new lib.ClipGroup_12_1();
        this.instance_12.parent = this;
        this.instance_12.setTransform(181.75, 99.95, 1, 1, 0, 0, 0, 11.7, 11.7);

        this.instance_13 = new lib.ClipGroup_13_1();
        this.instance_13.parent = this;
        this.instance_13.setTransform(146.8, 100.35, 1, 1, 0, 0, 0, 11.6, 11.6);

        this.instance_14 = new lib.ClipGroup_14_1();
        this.instance_14.parent = this;
        this.instance_14.setTransform(118.6, 114.5, 1, 1, 0, 0, 0, 11.8, 12);

        this.instance_15 = new lib.ClipGroup_15();
        this.instance_15.parent = this;
        this.instance_15.setTransform(306, 396, 1, 1, 0, 0, 0, 306, 396);

        var maskedShapeInstanceList = [this.instance, this.shape, this.shape_1, this.shape_2, this.shape_3, this.shape_4, this.shape_5, this.instance_1, this.instance_2, this.instance_3, this.instance_4, this.instance_5, this.instance_6, this.instance_7, this.instance_8, this.instance_9, this.instance_10, this.instance_11, this.instance_12, this.instance_13, this.instance_14, this.instance_15];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.instance_15 }, { t: this.instance_14 }, { t: this.instance_13 }, { t: this.instance_12 }, { t: this.instance_11 }, { t: this.instance_10 }, { t: this.instance_9 }, { t: this.instance_8 }, { t: this.instance_7 }, { t: this.instance_6 }, { t: this.instance_5 }, { t: this.instance_4 }, { t: this.instance_3 }, { t: this.instance_2 }, { t: this.instance_1 }, { t: this.shape_5 }, { t: this.shape_4 }, { t: this.shape_3 }, { t: this.shape_2 }, { t: this.shape_1 }, { t: this.shape }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.imaac_svg, new cjs.Rectangle(0, 0, 612, 792), null);


    (lib.cr_circle = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_3
        this.crDrop = new lib.crDrop();
        this.crDrop.name = "crDrop";
        this.crDrop.parent = this;
        this.crDrop.setTransform(11.05, 13.3, 1, 1, 0, 0, 0, 11.1, 14.1);

        this.timeline.addTween(cjs.Tween.get(this.crDrop).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cr_circle, new cjs.Rectangle(-107, -93.8, 217, 217), null);


    (lib.cbrn_pill = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.cbrnPill = new lib.cbrnPill();
        this.cbrnPill.name = "cbrnPill";
        this.cbrnPill.parent = this;
        this.cbrnPill.setTransform(0, -108.9);

        this.timeline.addTween(cjs.Tween.get(this.cbrnPill).wait(1));

    }).prototype = getMCSymbolPrototype(lib.cbrn_pill, new cjs.Rectangle(-478.5, -332.4, 960, 490), null);


    (lib.br_circle = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_3
        this.brDrop = new lib.brDrop();
        this.brDrop.name = "brDrop";
        this.brDrop.parent = this;
        this.brDrop.setTransform(-3.95, 7.7, 1, 1, 0, 0, 0, -3.9, 8.5);

        this.timeline.addTween(cjs.Tween.get(this.brDrop).wait(1));

    }).prototype = getMCSymbolPrototype(lib.br_circle, new cjs.Rectangle(-107, -93.8, 217, 217), null);


    (lib.back_circle = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_2
        this.backButton = new lib.backButton();
        this.backButton.name = "backButton";
        this.backButton.parent = this;
        this.backButton.setTransform(-181.75, 0.05);

        this.timeline.addTween(cjs.Tween.get(this.backButton).wait(1));

    }).prototype = getMCSymbolPrototype(lib.back_circle, new cjs.Rectangle(-260, -93, 371, 218), null);


    (lib.rrPill = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.rrLogoSlide = new lib._rrLogoSlide();
        this.rrLogoSlide.name = "rrLogoSlide";
        this.rrLogoSlide.parent = this;
        this.rrLogoSlide.setTransform(441.1, -19.6, 1, 1, 0, 0, 0, -29.7, -19.6);

        this.rrPillBevel = new lib._rrPillPrimative();
        this.rrPillBevel.name = "rrPillBevel";
        this.rrPillBevel.parent = this;
        this.rrPillBevel.shadow = new cjs.Shadow("#CCCCCC", 0, 4, 0);

        this.rrPillShadow = new lib._rrPillPrimative();
        this.rrPillShadow.name = "rrPillShadow";
        this.rrPillShadow.parent = this;
        this.rrPillShadow.shadow = new cjs.Shadow("rgba(0,0,0,0.349)", 0, 20, 62);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.rrPillShadow }, { t: this.rrPillBevel }, { t: this.rrLogoSlide }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.rrPill, new cjs.Rectangle(-1091.1, -223.5, 2138.1, 490), null);


    (lib.imDrop = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.instance = new lib.imaac_svg();
        this.instance.parent = this;
        this.instance.setTransform(0, 1.05, 0.3645, 0.3645, 0, 0, 0, 165.7, 167.8);

        this.white_circle_rr = new lib.white_circle();
        this.white_circle_rr.name = "white_circle_rr";
        this.white_circle_rr.parent = this;

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.white_circle_rr }, { t: this.instance }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.imDrop, new cjs.Rectangle(-107, -93, 269.7, 321.6), null);


    (lib.crPill = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.crLogoSlide = new lib._crLogoSlide();
        this.crLogoSlide.name = "crLogoSlide";
        this.crLogoSlide.parent = this;
        this.crLogoSlide.setTransform(441.1, -19.6, 1, 1, 0, 0, 0, -29.7, -19.6);

        this.crPillBevel = new lib._crPillPrimative();
        this.crPillBevel.name = "crPillBevel";
        this.crPillBevel.parent = this;
        this.crPillBevel.shadow = new cjs.Shadow("#CCCCCC", 0, 4, 0);

        this.crPillShadow = new lib._crPillPrimative();
        this.crPillShadow.name = "crPillShadow";
        this.crPillShadow.parent = this;
        this.crPillShadow.shadow = new cjs.Shadow("rgba(0,0,0,0.349)", 0, 20, 62);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.crPillShadow }, { t: this.crPillBevel }, { t: this.crLogoSlide }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.crPill, new cjs.Rectangle(-1228.9, -223.5, 2395.9, 490), null);


    (lib.brPill = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_1
        this.brLogoSlide = new lib._brLogoSlide();
        this.brLogoSlide.name = "brLogoSlide";
        this.brLogoSlide.parent = this;
        this.brLogoSlide.setTransform(441.1, -19.6, 1, 1, 0, 0, 0, -29.7, -19.6);

        this.brPillBevel = new lib._brPillPrimative();
        this.brPillBevel.name = "brPillBevel";
        this.brPillBevel.parent = this;
        this.brPillBevel.shadow = new cjs.Shadow("#CCCCCC", 0, 4, 0);

        this.brPillShadow = new lib._brPillPrimative();
        this.brPillShadow.name = "brPillShadow";
        this.brPillShadow.parent = this;
        this.brPillShadow.shadow = new cjs.Shadow("rgba(0,0,0,0.349)", 0, 20, 62);

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.brPillShadow }, { t: this.brPillBevel }, { t: this.brLogoSlide }] }).wait(1));

    }).prototype = getMCSymbolPrototype(lib.brPill, new cjs.Rectangle(-1108.2, -223.5, 2111.7, 490), null);


    (lib._rrPill = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // wordmark
        this.rrPill = new lib.rrPill();
        this.rrPill.name = "rrPill";
        this.rrPill.parent = this;
        this.rrPill.setTransform(-22.1, -9.3, 1, 1, 0, 0, 0, -22.1, -9.3);

        this.timeline.addTween(cjs.Tween.get(this.rrPill).wait(1));

    }).prototype = getMCSymbolPrototype(lib._rrPill, new cjs.Rectangle(-1091.1, -223.5, 2138.1, 490), null);


    (lib._crPill = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // wordmark
        this.crPill = new lib.crPill();
        this.crPill.name = "crPill";
        this.crPill.parent = this;
        this.crPill.setTransform(-31, 41.4, 1, 1, 0, 0, 0, -31, 41.4);

        this.timeline.addTween(cjs.Tween.get(this.crPill).wait(1));

    }).prototype = getMCSymbolPrototype(lib._crPill, new cjs.Rectangle(-1228.9, -223.5, 2395.9, 490), null);


    (lib._brPill = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, {});

        // wordmark
        this.brPill = new lib.brPill();
        this.brPill.name = "brPill";
        this.brPill.parent = this;
        this.brPill.setTransform(-52.4, 27.9, 1, 1, 0, 0, 0, -52.4, 27.9);

        this.timeline.addTween(cjs.Tween.get(this.brPill).wait(1));

    }).prototype = getMCSymbolPrototype(lib._brPill, new cjs.Rectangle(-1108.2, -223.5, 2111.7, 490), null);


    (lib.im_circle = function (mode, startPosition, loop) {
        this.initialize(mode, startPosition, loop, {});

        // Layer_3
        this.imDrop = new lib.imDrop();
        this.imDrop.name = "imDrop";
        this.imDrop.parent = this;
        this.imDrop.setTransform(-0.05, -0.8);

        this.timeline.addTween(cjs.Tween.get(this.imDrop).wait(1));

    }).prototype = getMCSymbolPrototype(lib.im_circle, new cjs.Rectangle(-107, -93.8, 269.7, 321.6), null);


    // stage content:
    (lib.RECOVER_cbrnSplashv12 = function (mode, startPosition, loop) {
        if (loop == null) { loop = false; } this.initialize(mode, startPosition, loop, { "cbrn start": 0, "cbrn started": 8, "rr expand": 9, "rr stop": 64, "rr contract": 70, "br expand": 109, "br stop": 167, "br contract": 170, "cr expand": 209, "cr stop": 264, "cr contract": 270, "im expand": 309, "im stop": 364, "im contract": 369, "cbrn expand": 410, "cbrn stop": 420, "cbrn contract": 428, "cbrn revert": 449 });

        // timeline functions:
        this.frame_0 = function () {
            /* Click to Go to Frame and Play
            Clicking on the specified symbol instance moves the playhead to the specified frame in the timeline and continues playback from that frame.
            Can be used on the main timeline or on movie clip timelines.
        	
            Instructions:
            1. Replace the number 5 in the code below with the frame number you would like the playhead to move to when the symbol instance is clicked.
            2.Frame numbers in EaselJS start at 0 instead of 1
            */
            var _this = this;
            _this.stop();

            var listOfObjects =
                [
                    _this.back_button.backButton,
                    _this.__rrPill.rrPill,
                    _this.__brPill.brPill,
                    _this.__crPill.crPill,
                    _this.rr_circle1.rrDrop,
                    _this.br_circle1.brDrop,
                    _this.cr_circle1.crDrop,
                    _this.im_circle1.imDrop,
                    _this.im_circle2.imDrop,
                    _this.cbrn_pill.cbrnPill,
                    _this.cbrn_pill_top.cbrnPill,
                    _this.rr_text.rrText,
                    _this.br_text.brText,
                    _this.cr_text.crText,
                    _this.im_text.imText, //dyn text
                    _this.rr_text_box.rrTextBox,
                    _this.br_text_box.brTextBox,
                    _this.cr_text_box.crTextBox,
                    _this.im_text_box.imTextBox,
                    _this.cbrn_text,
                    _this.reg_button,
                    _this.log_button,
                ];
            setListOfObjects(listOfObjects);
            storeScales(listOfObjects);
            windowResize();
            dropTick();

            var url = window.location.protocol + '//' + window.location.hostname + window.location.pathname;

            if (window.location.hash) {
                var hash = window.location.hash.substring(1); //Puts hash in variable, and removes the # character
                switch (hash) {
                    case 'main':
                        mainNoUrl();
                        break;
                    case 'cbrn':
                        cbrnText();
                        break;
                    case 'rad':
                        rrText();
                        break;
                    case 'bio':
                        brText();
                        break;
                    case 'chem':
                        crText();
                        break;
                    case 'imaac':
                        imText();
                        break;
                    default:
                        break;
                }
            } else {
                // No hash found
            }

            /* Click to Go to Frame and Play
            Clicking on the specified symbol instance moves the playhead to the specified frame in the timeline and continues playback from that frame.
            Can be used on the main timeline or on movie clip timelines.
        	
            Instructions:
            1. Replace the number 5 in the code below with the frame number you would like the playhead to move to when the symbol instance is clicked.
            2.Frame numbers in EaselJS start at 0 instead of 1
            */


            this.cbrn_pill_top.addEventListener("click", main.bind(this));

            this.back_button.addEventListener("click", main.bind(this));
            this.back_button.backButton.back_button.addEventListener("click", main.bind(this));
            this.back_button.cursor = "pointer";

            this.cbrn_pill.addEventListener("click", cbrnAnim.bind(this));
            this.rr_circle1.addEventListener("click", rrAnim.bind(this));
            this.rr_circle1.cursor = "pointer";
            this.br_circle1.addEventListener("click", brAnim.bind(this));
            this.br_circle1.cursor = "pointer";
            this.cr_circle1.addEventListener("click", crAnim.bind(this));
            this.cr_circle1.cursor = "pointer";
            this.im_circle1.addEventListener("click", imAnim.bind(this));
            this.testimaac.addEventListener("click", imAnim.bind(this));

            this.reg_button.addEventListener("click", reg_Click.bind(this));
            this.log_button.addEventListener("click", log_Click.bind(this));



            function main() {
                // console.log('back');
                addTick();
                collapse(_this, 'cbrn');
                //window.open( url + '#main', '_self' );
                //_this.gotoAndStop('cbrn started');
            }

            function mainNoUrl() {
                addTick();
                _this.gotoAndStop('cbrn started');
            }

            function cbrnAnim() {
                addTick();
                //collapse(_this,'cbrn');
                //window.open( url + '#cbrn', '_self' );
                _this.gotoAndPlay('cbrn expand');
            }

            function rrAnim() {
                addTick();
                collapse(_this, 'rr');
                //window.open( url + '#rad', '_self' );
                //_this.gotoAndPlay('rr expand');
            }

            function brAnim() {
                addTick();
                collapse(_this, 'br');
                //window.open( url + '#bio', '_self' );
                //_this.gotoAndPlay('br expand');
            }

            function crAnim() {
                addTick();
                collapse(_this, 'cr');
                //window.open( url + '#chem', '_self' );
                //_this.gotoAndPlay('cr expand');
            }

            function imAnim() {
                addTick();
                collapse(_this, 'im');
                //window.open( url + '#imaac', '_self' );
                //_this.gotoAndPlay('im expand');
            }

            function cbrnText() {
                _this.gotoAndStop('cbrn stop');
            }

            function rrText() {
                _this.gotoAndPlay('rr stop');
            }

            function brText() {
                _this.gotoAndStop('br stop');
            }

            function crText() {
                _this.gotoAndStop('cr stop');
            }

            function imText() {
                _this.gotoAndStop('im stop');
            }

            function reg_Click() {
                window.open('/app/index#account/request', '_self');
            }

            function log_Click() {
                window.open('/app/signin', '_self');
            }
        }
        this.frame_1 = function () {
            //createjs.Ticker.removeEventListener('tick', stage);
        }
        this.frame_8 = function () {
            this.stop();
            dropTick();
        }
        this.frame_40 = function () {
            this.__rrPill.rrPill.rrLogoSlide.gotoAndStop(1);
            this.__rrPill.rrPill.rrPillBevel.gotoAndStop(1);
            this.__rrPill.rrPill.rrPillShadow.gotoAndStop(1);
        }
        this.frame_41 = function () {
            this.__rrPill.rrPill.rrLogoSlide.gotoAndPlay('open');
            this.__rrPill.rrPill.rrPillBevel.gotoAndPlay('open');
            this.__rrPill.rrPill.rrPillShadow.gotoAndPlay('open');
        }
        this.frame_64 = function () {
            this.__rrPill.rrPill.rrLogoSlide.gotoAndStop('opened');
            this.__rrPill.rrPill.rrPillBevel.gotoAndStop('opened');
            this.__rrPill.rrPill.rrPillShadow.gotoAndStop('opened');
            this.__rrPill.rrPill.rrLogoSlide.gotoAndStop('opened');
            this.__rrPill.rrPill.rrPillBevel.gotoAndStop('opened');
            this.__rrPill.rrPill.rrPillShadow.gotoAndStop('opened');
        }
        this.frame_65 = function () {
            this.stop();
            dropTick();
        }
        this.frame_70 = function () {
            this.__rrPill.rrPill.rrLogoSlide.gotoAndPlay('close');
            this.__rrPill.rrPill.rrPillBevel.gotoAndPlay('close');
            this.__rrPill.rrPill.rrPillShadow.gotoAndPlay('close');
        }
        this.frame_99 = function () {
            if (slide == 'cbrn') {
                this.gotoAndStop('cbrn started');
            } else {
                expand(this, slide);
            }
        }
        this.frame_148 = function () {
            this.__brPill.brPill.brLogoSlide.gotoAndStop(1);
            this.__brPill.brPill.brPillBevel.gotoAndStop(1);
            this.__brPill.brPill.brPillShadow.gotoAndStop(1);
        }
        this.frame_149 = function () {
            this.__brPill.brPill.brLogoSlide.gotoAndPlay('open');
            this.__brPill.brPill.brPillBevel.gotoAndPlay('open');
            this.__brPill.brPill.brPillShadow.gotoAndPlay('open');
        }
        this.frame_167 = function () {
            this.stop();
            dropTick();
        }
        this.frame_173 = function () {
            this.__brPill.brPill.brLogoSlide.gotoAndPlay('close');
            this.__brPill.brPill.brPillBevel.gotoAndPlay('close');
            this.__brPill.brPill.brPillShadow.gotoAndPlay('close');
        }
        this.frame_199 = function () {
            revert(this);
        }
        this.frame_228 = function () {
            this.__crPill.crPill.crLogoSlide.gotoAndStop(1);
            this.__crPill.crPill.crPillBevel.gotoAndStop(1);
            this.__crPill.crPill.crPillShadow.gotoAndStop(1);
        }
        this.frame_229 = function () {
            this.__crPill.crPill.crLogoSlide.gotoAndPlay('open');
            this.__crPill.crPill.crPillBevel.gotoAndPlay('open');
            this.__crPill.crPill.crPillShadow.gotoAndPlay('open');
        }
        this.frame_264 = function () {
            this.stop();
            dropTick();
        }
        this.frame_270 = function () {
            this.__crPill.crPill.crLogoSlide.gotoAndPlay('close');
            this.__crPill.crPill.crPillBevel.gotoAndPlay('close');
            this.__crPill.crPill.crPillShadow.gotoAndPlay('close');
        }
        this.frame_305 = function () {
            revert(this);
        }
        this.frame_364 = function () {
            this.stop();
            dropTick();
        }
        this.frame_406 = function () {
            revert(this);
        }
        this.frame_420 = function () {
            this.stop();
            dropTick();
        }
        this.frame_440 = function () {
            this.gotoAndStop('cbrn started');
        }
        this.frame_465 = function () {
            if (slide == 'cbrn') {
                this.gotoAndStop('cbrn started')
            } else {
                expand(this, slide);
            }
        }

        // actions tween:
        this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(1).call(this.frame_1).wait(7).call(this.frame_8).wait(32).call(this.frame_40).wait(1).call(this.frame_41).wait(23).call(this.frame_64).wait(1).call(this.frame_65).wait(5).call(this.frame_70).wait(29).call(this.frame_99).wait(49).call(this.frame_148).wait(1).call(this.frame_149).wait(18).call(this.frame_167).wait(6).call(this.frame_173).wait(26).call(this.frame_199).wait(29).call(this.frame_228).wait(1).call(this.frame_229).wait(35).call(this.frame_264).wait(6).call(this.frame_270).wait(35).call(this.frame_305).wait(59).call(this.frame_364).wait(42).call(this.frame_406).wait(14).call(this.frame_420).wait(20).call(this.frame_440).wait(25).call(this.frame_465).wait(35));

        // test2
        this.testimaac = new lib.testimaac();
        this.testimaac.name = "testimaac";
        this.testimaac.parent = this;
        this.testimaac.setTransform(899.65, 122.2);
        new cjs.ButtonHelper(this.testimaac, 0, 1, 2, false, new lib.testimaac(), 3);

        this.timeline.addTween(cjs.Tween.get(this.testimaac).to({ _off: true }, 315).wait(96).to({ _off: false }, 0).wait(89));

        // rr_pill2
        this.__rrPill = new lib._rrPill();
        this.__rrPill.name = "__rrPill";
        this.__rrPill.parent = this;
        this.__rrPill.setTransform(461.3, 433.55, 0.5868, 0.5868, 0, 0, 0, 78.8, 0.1);
        this.__rrPill._off = true;

        this.timeline.addTween(cjs.Tween.get(this.__rrPill).wait(40).to({ _off: false }, 0).wait(1).to({ x: 303.8 }, 8).wait(6).to({ regX: 79, regY: 0.3, scaleX: 0.3931, scaleY: 0.3931, x: 163.9, y: 250.1 }, 3).wait(1).to({ x: 150.4, y: 242 }, 0).wait(1).to({ x: 163.9, y: 250.1 }, 0).to({ _off: true }, 21).wait(419));

        // rr_text
        this.rr_text = new lib.rr_text();
        this.rr_text.name = "rr_text";
        this.rr_text.parent = this;
        this.rr_text.setTransform(132.85, 250, 1, 1, 0, 0, 0, -60.7, -87.7);
        this.rr_text.alpha = 0;
        this.rr_text._off = true;

        this.timeline.addTween(cjs.Tween.get(this.rr_text).wait(61).to({ _off: false }, 0).to({ regY: -107.7, y: 251, alpha: 1 }, 2).wait(7).to({ regY: -108.2, y: 250.5 }, 0).to({ regY: -108.7, y: 250, alpha: 0 }, 2).to({ _off: true }, 26).wait(402));

        // rr_dropdown
        this.rr_text_box = new lib.rr_text_box();
        this.rr_text_box.name = "rr_text_box";
        this.rr_text_box.parent = this;
        this.rr_text_box.setTransform(132.85, 249.95, 1, 1, 0, 0, 0, -182.3, -39);
        this.rr_text_box._off = true;

        this.timeline.addTween(cjs.Tween.get(this.rr_text_box).wait(58).to({ _off: false }, 0).to({ regX: -184.3, regY: -25.9, scaleY: 10.25, y: 251.05 }, 5).wait(7).to({ regX: -182.3, regY: -39, scaleY: 1, y: 249.95 }, 5).to({ _off: true }, 1).wait(424));

        // br_pill2
        this.__brPill = new lib._brPill();
        this.__brPill.name = "__brPill";
        this.__brPill.parent = this;
        this.__brPill.setTransform(379.5, 252.95, 0.3428, 0.3428, 0, 0, 0, -11.8, 28.3);
        this.__brPill._off = true;

        this.timeline.addTween(cjs.Tween.get(this.__brPill).wait(148).to({ _off: false }, 0).wait(1).to({ scaleX: 0.3734, scaleY: 0.3734, x: 260.5, y: 250.1 }, 6).to({ regX: -11.7, scaleX: 0.3887, scaleY: 0.3887, x: 201, y: 248.7 }, 3).wait(15).to({ x: 381 }, 8).wait(3).to({ regX: -11.6, regY: 28.4, x: 381.05, y: 170.5 }, 0).to({ regX: -11.7, regY: 28.3, x: 381, y: -64.3 }, 3).to({ _off: true }, 15).wait(298));

        // br_text
        this.br_text = new lib.br_text();
        this.br_text.name = "br_text";
        this.br_text.parent = this;
        this.br_text.setTransform(387.25, 406.65, 1, 1, 0, 0, 0, 123, 96.4);
        this.br_text.alpha = 0;
        this.br_text._off = true;

        this.timeline.addTween(cjs.Tween.get(this.br_text).wait(162).to({ _off: false }, 0).to({ y: 422.4, alpha: 0.75 }, 3).to({ regX: -21.1, regY: -70.2, x: 243.15, y: 261.05, alpha: 1 }, 1).wait(3).to({ regX: 123, regY: 96.4, x: 387.25, y: 427.65 }, 0).to({ y: 406.65, alpha: 0 }, 3).to({ _off: true }, 30).wait(298));

        // br_dropdown
        this.br_text_box = new lib.br_text_box();
        this.br_text_box.name = "br_text_box";
        this.br_text_box.parent = this;
        this.br_text_box.setTransform(201.95, 239.45, 1.1724, 0.7766, 0, 0, 0, -161.5, -24.6);
        this.br_text_box._off = true;

        this.timeline.addTween(cjs.Tween.get(this.br_text_box).wait(159).to({ _off: false }, 0).to({ regY: -24.5, scaleY: 10.7779, y: 239.5 }, 6).wait(4).to({ regY: -24.6, scaleY: 0.7766, y: 239.45 }, 5).to({ _off: true }, 1).wait(325));

        // cr_pill2
        this.__crPill = new lib._crPill();
        this.__crPill.name = "__crPill";
        this.__crPill.parent = this;
        this.__crPill.setTransform(628.95, 286.3, 0.3479, 0.3479, 0, 0, 0, -30.9, 41.7);
        this.__crPill._off = true;

        this.timeline.addTween(cjs.Tween.get(this.__crPill).wait(228).to({ _off: false }, 0).wait(1).to({ regX: -31, regY: 41.6, scaleX: 0.3887, scaleY: 0.3887, x: 518.95, y: 281.25 }, 9).wait(35).to({ x: 628.95 }, 4).wait(3).to({ y: -58.75 }, 5).wait(215));

        // cr_text
        this.cr_text = new lib.cr_text();
        this.cr_text.name = "cr_text";
        this.cr_text.parent = this;
        this.cr_text.setTransform(572.15, 269.75, 1, 1, 0, 0, 0, -40.9, -88.5);
        this.cr_text.alpha = 0;
        this.cr_text._off = true;

        this.timeline.addTween(cjs.Tween.get(this.cr_text).wait(243).to({ _off: false }, 0).to({ regX: -40.5, regY: -99.4, x: 572.55, y: 269.25, alpha: 0.8281 }, 5).to({ regY: -101.5, alpha: 1 }, 1).wait(21).to({ regY: -89 }, 0).to({ regX: -41.1, regY: -89.9, x: 571.95, y: 268.35, alpha: 0 }, 4).to({ _off: true }, 29).wait(197));

        // cr_dropdown
        this.cr_text_box = new lib.cr_text_box();
        this.cr_text_box.name = "cr_text_box";
        this.cr_text_box.parent = this;
        this.cr_text_box.setTransform(572.55, 269.25, 0.9999, 0.8775, 0, 0, 0, -161.4, -24.6);
        this.cr_text_box._off = true;

        this.timeline.addTween(cjs.Tween.get(this.cr_text_box).wait(241).to({ _off: false }, 0).to({ regX: -161.3, scaleY: 10.1508, x: 572.65 }, 5).wait(24).to({ regX: -161.4, scaleY: 0.8775, x: 572.55 }, 3).to({ _off: true }, 1).wait(226));

        // im
        this.im_circle2 = new lib.im_circle();
        this.im_circle2.name = "im_circle2";
        this.im_circle2.parent = this;
        this.im_circle2.setTransform(900.2, 318.4);
        this.im_circle2._off = true;

        this.timeline.addTween(cjs.Tween.get(this.im_circle2).wait(334).to({ _off: false }, 0).to({ scaleX: 1.1399, scaleY: 1.1399, x: 801.45 }, 2).to({ scaleX: 1.4895, scaleY: 1.4895, x: 554.5 }, 5).wait(34).to({ scaleX: 1, scaleY: 1, x: 900.2 }, 6).wait(2).to({ x: 900.5, y: -65.55 }, 7).to({ _off: true }, 20).wait(90));

        // im_text
        this.im_text = new lib.im_text();
        this.im_text.name = "im_text";
        this.im_text.parent = this;
        this.im_text.setTransform(553.65, 318.15, 1, 1, 0, 0, 0, -34.6, -46.1);
        this.im_text.alpha = 0;
        this.im_text._off = true;

        this.timeline.addTween(cjs.Tween.get(this.im_text).wait(338).to({ _off: false }, 0).to({ x: 554.4, y: 318.4, alpha: 0.8594 }, 6).to({ regX: -33.7, regY: -66.7, x: 554.55, y: 318.55, alpha: 1 }, 1).wait(24).to({ regY: -66.8, y: 318.45 }, 0).to({ regX: -34.6, regY: -46.1, x: 553.65, y: 318.15, alpha: 0 }, 7).to({ _off: true }, 34).wait(90));

        // im_dropdown
        this.im_text_box = new lib.im_text_box();
        this.im_text_box.name = "im_text_box";
        this.im_text_box.parent = this;
        this.im_text_box.setTransform(553.7, 318.1, 1.0003, 0.8854, 0, 0, 0, -161.5, -24.6);
        this.im_text_box._off = true;

        this.timeline.addTween(cjs.Tween.get(this.im_text_box).wait(338).to({ _off: false }, 0).to({ regX: -160.7, scaleY: 9.1563, x: 554.5 }, 6).wait(25).to({ regX: 161.6, x: 876.9 }, 0).to({ scaleY: 0.8854 }, 7).to({ regX: 0, regY: 0, scaleX: 0.2199, x: 793.85, y: 339.9 }, 3).to({ _off: true }, 1).wait(120));

        // back
        this.back_button = new lib.back_circle();
        this.back_button.name = "back_button";
        this.back_button.parent = this;
        this.back_button.setTransform(-46, 67.6, 0.7164, 0.7164);
        this.back_button._off = true;

        this.timeline.addTween(cjs.Tween.get(this.back_button).wait(27).to({ _off: false }, 0).to({ x: 122 }, 3).wait(40).to({ x: -46 }, 3).to({ _off: true }, 15).wait(58).to({ _off: false }, 0).to({ x: 122 }, 3).to({ _off: true }, 60).wait(37).to({ _off: false, x: -46 }, 0).to({ x: 122 }, 3).wait(59).to({ _off: true }, 1).wait(37).to({ _off: false, x: -46 }, 0).to({ x: 122 }, 3).to({ _off: true }, 61).wait(90));

        // rr
        this.rr_circle1 = new lib.rr_circle();
        this.rr_circle1.name = "rr_circle1";
        this.rr_circle1.parent = this;
        this.rr_circle1.setTransform(122, 122.6);

        this.timeline.addTween(cjs.Tween.get(this.rr_circle1).wait(9).to({ y: 462.1 }, 13, cjs.Ease.cubicIn).to({ y: 433.95 }, 5).to({ x: 422 }, 8).to({ x: 415 }, 4).to({ _off: true }, 2).wait(40).to({ _off: false, regX: 0.1, regY: -61.8, scaleX: 1.1345, scaleY: 1.1345, x: 131.5, y: 180.8 }, 0).to({ scaleX: 0.7162, scaleY: 0.7162, x: 122.05, y: 175.65 }, 3).to({ y: 23.9 }, 3).wait(4).to({ regX: 0, regY: 0, x: 122, y: 68.15 }, 0).to({ scaleX: 1, scaleY: 1, y: 122.6 }, 6).wait(36).to({ scaleX: 0.7164, scaleY: 0.7164, y: 67.6 }, 3).wait(9).to({ x: 383.6 }, 4).wait(21).to({ y: -50 }, 6).wait(33).to({ scaleX: 1, scaleY: 1, x: 122, y: 122.6 }, 0).wait(30).to({ scaleX: 0.7164, scaleY: 0.7164, y: 67.6 }, 3).wait(3).to({ x: 383.55 }, 4).wait(25).to({ y: -47.9 }, 6).wait(29).to({ scaleX: 1, scaleY: 1, x: 122, y: 122.6 }, 0).wait(24).to({ scaleX: 0.7164, scaleY: 0.7164, y: 67.6 }, 3).wait(9).to({ x: 383.6, y: 69.4 }, 4).wait(27).to({ y: -48.2 }, 5).wait(29).to({ scaleX: 1, scaleY: 1, x: 122, y: 122.6 }, 0).wait(39).to({ y: -64.4 }, 0).to({ y: 122.6 }, 7).wait(44));

        // br
        this.br_circle1 = new lib.br_circle();
        this.br_circle1.name = "br_circle1";
        this.br_circle1.parent = this;
        this.br_circle1.setTransform(383.6, 122);

        this.timeline.addTween(cjs.Tween.get(this.br_circle1).wait(28).to({ scaleX: 0.7164, scaleY: 0.7164, x: 383.55, y: 67.6 }, 3).wait(60).to({ scaleX: 1, scaleY: 1, x: 383.6, y: 122 }, 6).wait(12).to({ y: 274 }, 13, cjs.Ease.cubicIn).to({ y: 244.2 }, 5).to({ _off: true }, 22).wait(60).to({ _off: false, y: 122 }, 0).wait(27).to({ scaleX: 0.7164, scaleY: 0.7164, x: 383.55, y: 67.6 }, 3).wait(5).to({ x: 640.55 }, 4).wait(26).to({ y: -47.9 }, 6).wait(29).to({ scaleX: 1, scaleY: 1, x: 383.6, y: 122 }, 0).wait(22).to({ scaleX: 0.7164, scaleY: 0.7164, x: 383.55, y: 67.6 }, 3).wait(10).to({ x: 640.5, y: 69.4 }, 4).wait(28).to({ y: -48.2 }, 5).wait(29).to({ scaleX: 1, scaleY: 1, x: 383.6, y: 122 }, 0).to({ _off: true }, 39).wait(2).to({ _off: false, y: -65 }, 0).to({ y: 122 }, 7).wait(42));

        // cr
        this.cr_circle1 = new lib.cr_circle();
        this.cr_circle1.name = "cr_circle1";
        this.cr_circle1.parent = this;
        this.cr_circle1.setTransform(640.55, 122);

        this.timeline.addTween(cjs.Tween.get(this.cr_circle1).wait(30).to({ scaleX: 0.7164, scaleY: 0.7164, x: 640.5, y: 67.6 }, 3).wait(58).to({ scaleX: 1, scaleY: 1, x: 640.55, y: 122 }, 6).wait(39).to({ scaleX: 0.7164, scaleY: 0.7164, x: 640.5, y: 67.6 }, 3).wait(31).to({ y: -50 }, 6).wait(33).to({ scaleX: 1, scaleY: 1, x: 640.55, y: 122 }, 0).to({ y: 312 }, 13, cjs.Ease.cubicIn).to({ y: 271.55 }, 5).to({ _off: true }, 1).wait(81).to({ _off: false, y: 122 }, 0).wait(20).to({ scaleX: 0.7164, scaleY: 0.7164, x: 640.5, y: 67.6 }, 3).wait(11).to({ x: 902.1, y: 69.4 }, 4).wait(29).to({ y: -48.2 }, 5).wait(29).to({ scaleX: 1, scaleY: 1, x: 640.55, y: 122 }, 0).to({ _off: true }, 39).wait(4).to({ _off: false, y: -65 }, 0).to({ y: 122 }, 7).wait(40));

        // im
        this.im_circle1 = new lib.im_circle();
        this.im_circle1.name = "im_circle1";
        this.im_circle1.parent = this;
        this.im_circle1.setTransform(900.2, 122);

        this.timeline.addTween(cjs.Tween.get(this.im_circle1).wait(31).to({ scaleX: 0.7164, scaleY: 0.7164, y: 67.6 }, 3).wait(57).to({ scaleX: 1, scaleY: 1, y: 122 }, 6).wait(42).to({ scaleX: 0.7164, scaleY: 0.7164, y: 67.6 }, 3).wait(28).to({ y: -50 }, 6).wait(33).to({ scaleX: 1, scaleY: 1, y: 122 }, 0).wait(24).to({ scaleX: 0.7164, scaleY: 0.7164, y: 67.6 }, 3).wait(38).to({ y: -47.9 }, 6).wait(29).to({ scaleX: 1, scaleY: 1, y: 122 }, 0).wait(6).to({ y: 348 }, 13, cjs.Ease.cubicIn).to({ y: 318.4 }, 5).to({ _off: true }, 1).wait(76).to({ _off: false, y: 122 }, 0).to({ _off: true }, 39).wait(6).to({ _off: false, y: -65 }, 0).to({ y: 122 }, 7).wait(38));

        // green_menu
        this.shape = new cjs.Shape();
        this.shape.graphics.f("#86BE39").s().p("Az/FPIAAqdMAn/AAAIAAKdg");
        this.shape.setTransform(128, -33.9);

        this.instance = new lib.Tween13("synched", 0);
        this.instance.parent = this;
        this.instance.setTransform(128, -33.9);
        this.instance._off = true;

        this.instance_1 = new lib.Tween14("synched", 0);
        this.instance_1.parent = this;
        this.instance_1.setTransform(128, -33.9);

        this.instance_2 = new lib.green_menu();
        this.instance_2.parent = this;
        this.instance_2.setTransform(128.05, -33.9);
        this.instance_2._off = true;

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape }] }).to({ state: [{ t: this.instance }] }, 91).to({ state: [{ t: this.instance_1 }] }, 6).to({ state: [{ t: this.shape }] }, 12).to({ state: [{ t: this.instance_2 }] }, 24).to({ state: [{ t: this.instance_2 }] }, 3).to({ state: [{ t: this.instance_2 }] }, 9).to({ state: [{ t: this.instance_2 }] }, 4).to({ state: [{ t: this.instance_2 }] }, 21).to({ state: [{ t: this.instance_2 }] }, 6).to({ state: [{ t: this.shape }] }, 33).to({ state: [{ t: this.instance_2 }] }, 30).to({ state: [{ t: this.instance_2 }] }, 3).to({ state: [{ t: this.instance_2 }] }, 3).to({ state: [{ t: this.instance_2 }] }, 4).to({ state: [{ t: this.instance_2 }] }, 25).to({ state: [{ t: this.instance_2 }] }, 6).to({ state: [{ t: this.shape }] }, 29).to({ state: [{ t: this.instance_2 }] }, 24).to({ state: [{ t: this.instance_2 }] }, 3).to({ state: [{ t: this.instance_2 }] }, 9).to({ state: [{ t: this.instance_2 }] }, 4).to({ state: [{ t: this.instance_2 }] }, 27).to({ state: [{ t: this.instance_2 }] }, 5).to({ state: [] }, 29).wait(90));
        this.timeline.addTween(cjs.Tween.get(this.instance).wait(91).to({ _off: false }, 0).to({ _off: true }, 6).wait(403));
        this.timeline.addTween(cjs.Tween.get(this.instance_2).wait(133).to({ _off: false }, 0).to({ x: 128, y: 33.5, mode: "synched", startPosition: 0 }, 3).wait(9).to({ startPosition: 0 }, 0).to({ x: 384.05, y: 33.7 }, 4).wait(21).to({ startPosition: 0 }, 0).to({ y: -84.1 }, 6).to({ _off: true }, 33).wait(30).to({ _off: false, x: 128, y: -33.9 }, 0).to({ scaleY: 1.006, y: 33.7 }, 3).wait(3).to({ scaleY: 1, y: 33.9 }, 0).to({ scaleY: 1.006, x: 384, y: 33.7 }, 4).wait(25).to({ scaleY: 1, y: 33.5 }, 0).to({ y: -82 }, 6).to({ _off: true }, 29).wait(24).to({ _off: false, x: 128, y: -33.9 }, 0).to({ scaleY: 1.006, y: 33.7 }, 3).wait(9).to({ scaleY: 1, y: 33.9 }, 0).to({ x: 383.95 }, 4).wait(27).to({ startPosition: 0 }, 0).to({ y: -83.7 }, 5).to({ _off: true }, 29).wait(90));

        // red_menu
        this.red_clip1 = new lib.red();
        this.red_clip1.name = "red_clip1";
        this.red_clip1.parent = this;
        this.red_clip1.setTransform(512.05, -67.4, 0.4999, 0.0877, 0, 0, 0, 256.1, -384.3);

        this.timeline.addTween(cjs.Tween.get(this.red_clip1).wait(28).to({ y: 0 }, 3).wait(60).to({ y: -67.7 }, 6).wait(12).to({ y: -67.4 }, 0).wait(127).to({ y: 0 }, 3).wait(5).to({ x: 768.05 }, 4).wait(26).to({ y: -115.5 }, 6).wait(29).to({ x: 512.05, y: -67.4 }, 0).wait(22).to({ y: 0 }, 3).wait(10).to({ x: 768 }, 4).wait(28).to({ y: -117.6 }, 5).to({ _off: true }, 29).wait(90));

        // orange_menu
        this.orange_clip1 = new lib.orange();
        this.orange_clip1.name = "orange_clip1";
        this.orange_clip1.parent = this;
        this.orange_clip1.setTransform(768.05, -67.4, 0.3333, 0.0877, 0, 0, 0, 384, -384.2);

        this.timeline.addTween(cjs.Tween.get(this.orange_clip1).wait(30).to({ y: 0 }, 3).wait(58).to({ y: -67.7 }, 6).wait(12).to({ y: -67.4 }, 0).wait(27).to({ y: 0 }, 3).wait(31).to({ y: -117.6 }, 6).wait(37).to({ y: -67.4 }, 0).wait(116).to({ y: 0 }, 3).wait(11).to({ x: 1024 }, 4).wait(29).to({ y: -117.6 }, 5).to({ _off: true }, 29).wait(90));

        // blue_menu
        this.blue_clip1 = new lib.blue();
        this.blue_clip1.name = "blue_clip1";
        this.blue_clip1.parent = this;
        this.blue_clip1.setTransform(1024.05, -67.35, 0.25, 0.0876, 0, 0, 0, 512.1, -384);

        this.timeline.addTween(cjs.Tween.get(this.blue_clip1).wait(32).to({ y: 0.05 }, 3).wait(56).to({ y: -67.65 }, 6).wait(12).to({ y: -67.35 }, 0).wait(30).to({ y: 0.05 }, 3).wait(28).to({ y: -117.55 }, 6).wait(37).to({ y: -67.35 }, 0).wait(20).to({ y: 0.05 }, 3).wait(38).to({ y: -115.45 }, 6).wait(29).to({ y: -67.35 }, 0).to({ _off: true }, 101).wait(90));

        // br_red_top (mask)
        var mask = new cjs.Shape();
        mask._off = true;
        var mask_graphics_109 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_110 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_111 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_112 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_113 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_114 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_115 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_116 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_117 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_118 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_119 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_120 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_121 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_122 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_127 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_128 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_graphics_129 = new cjs.Graphics().p("EgAEA8DMAAAh4FMAoHAAAMAAAB4Fg");
        var mask_graphics_130 = new cjs.Graphics().p("EgAlA8DMAAAh4FMAo4AAAMAAAB4Fg");
        var mask_graphics_131 = new cjs.Graphics().p("EgCfA8DMAAAh4FMArvAAAMAAAB4Fg");
        var mask_graphics_132 = new cjs.Graphics().p("EgHnA8DMAAAh4FMAzbAAAMAAAB4Fg");
        var mask_graphics_133 = new cjs.Graphics().p("EgS+A8DMAAAh4FMBEeAAAMAAAB4Fg");
        var mask_graphics_134 = new cjs.Graphics().p("EgpBA8DMAAAh4FMBliAAAMAAAB4Fg");
        var mask_graphics_135 = new cjs.Graphics().p("EhP/A8DMAAAh4FMCf/AAAMAAAB4Fg");
        var mask_graphics_170 = new cjs.Graphics().p("EhP/A8DMAAAh4FMCf/AAAMAAAB4Fg");
        var mask_graphics_171 = new cjs.Graphics().p("EhCpA8DMAAAh4FMCL+AAAMAAAB4Fg");
        var mask_graphics_172 = new cjs.Graphics().p("Eg1UA8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_graphics_173 = new cjs.Graphics().p("Egn/A8DMAAAh4FMBj/AAAMAAAB4Fg");
        var mask_graphics_174 = new cjs.Graphics().p("EgapA8DMAAAh4FMBP/AAAMAAAB4Fg");
        var mask_graphics_175 = new cjs.Graphics().p("EgNUA8DMAAAh4FMA7/AAAMAAAB4Fg");
        var mask_graphics_176 = new cjs.Graphics().p("EAAAA8DMAAAh4FMAoAAAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask).to({ graphics: null, x: 0, y: 0 }).wait(109).to({ graphics: mask_graphics_109, x: 256.0125, y: -384.325 }).wait(1).to({ graphics: mask_graphics_110, x: 256.0125, y: -325.225 }).wait(1).to({ graphics: mask_graphics_111, x: 256.0125, y: -266.075 }).wait(1).to({ graphics: mask_graphics_112, x: 256.0125, y: -206.925 }).wait(1).to({ graphics: mask_graphics_113, x: 256.0125, y: -147.825 }).wait(1).to({ graphics: mask_graphics_114, x: 256.0125, y: -88.675 }).wait(1).to({ graphics: mask_graphics_115, x: 256.0125, y: -29.575 }).wait(1).to({ graphics: mask_graphics_116, x: 256.0125, y: 29.575 }).wait(1).to({ graphics: mask_graphics_117, x: 256.0125, y: 88.675 }).wait(1).to({ graphics: mask_graphics_118, x: 256.0125, y: 147.825 }).wait(1).to({ graphics: mask_graphics_119, x: 256.0125, y: 206.925 }).wait(1).to({ graphics: mask_graphics_120, x: 256.0125, y: 266.075 }).wait(1).to({ graphics: mask_graphics_121, x: 256.0125, y: 325.225 }).wait(1).to({ graphics: mask_graphics_122, x: 256.0125, y: 384.325 }).wait(5).to({ graphics: mask_graphics_127, x: 256.0125, y: 384.325 }).wait(1).to({ graphics: mask_graphics_128, x: 256.0184, y: 384.325 }).wait(1).to({ graphics: mask_graphics_129, x: 256.275, y: 384.325 }).wait(1).to({ graphics: mask_graphics_130, x: 257.9116, y: 384.325 }).wait(1).to({ graphics: mask_graphics_131, x: 264.0137, y: 384.325 }).wait(1).to({ graphics: mask_graphics_132, x: 280.4247, y: 384.325 }).wait(1).to({ graphics: mask_graphics_133, x: 316.753, y: 384.325 }).wait(1).to({ graphics: mask_graphics_134, x: 387.3216, y: 384.35 }).wait(1).to({ graphics: mask_graphics_135, x: 512.0227, y: 384.325 }).wait(35).to({ graphics: mask_graphics_170, x: 512.0227, y: 384.325 }).wait(1).to({ graphics: mask_graphics_171, x: 469.3394, y: 384.325 }).wait(1).to({ graphics: mask_graphics_172, x: 426.716, y: 384.275 }).wait(1).to({ graphics: mask_graphics_173, x: 384.0387, y: 384.275 }).wait(1).to({ graphics: mask_graphics_174, x: 341.3613, y: 384.275 }).wait(1).to({ graphics: mask_graphics_175, x: 298.7109, y: 384.225 }).wait(1).to({ graphics: mask_graphics_176, x: 256.0125, y: 384.325 }).wait(33).to({ graphics: null, x: 0, y: 0 }).wait(291));

        // br_tint
        this.instance_3 = new lib.br_tint();
        this.instance_3.parent = this;
        this.instance_3.setTransform(512, 384.1, 1, 1, 0, 0, 0, 512, 384.5);
        this.instance_3._off = true;

        var maskedShapeInstanceList = [this.instance_3];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_3).wait(109).to({ _off: false }, 0).wait(20).to({ alpha: 0 }, 5).wait(36).to({ alpha: 1 }, 5).to({ _off: true }, 34).wait(291));

        // br_bkg
        this.instance_4 = new lib.BR_bkg();
        this.instance_4.parent = this;
        this.instance_4.setTransform(0, 0, 0.7986, 0.7988);
        this.instance_4._off = true;

        var maskedShapeInstanceList = [this.instance_4];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_4).wait(109).to({ _off: false }, 0).to({ _off: true }, 100).wait(291));

        // cr_orange_top (mask)
        var mask_1 = new cjs.Shape();
        mask_1._off = true;
        var mask_1_graphics_209 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_210 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_211 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_212 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_213 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_214 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_215 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_216 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_217 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_218 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_219 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_220 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_221 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_222 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_227 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_228 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_229 = new cjs.Graphics().p("EAT6A8DMAAAh4FMAoIAAAMAAAB4Fg");
        var mask_1_graphics_230 = new cjs.Graphics().p("EATRA8DMAAAh4FMAo5AAAMAAAB4Fg");
        var mask_1_graphics_231 = new cjs.Graphics().p("EAQ4A8DMAAAh4FMArwAAAMAAAB4Fg");
        var mask_1_graphics_232 = new cjs.Graphics().p("EAKeA8DMAAAh4FMAzcAAAMAAAB4Fg");
        var mask_1_graphics_233 = new cjs.Graphics().p("EgDuA8DMAAAh4FMBEeAAAMAAAB4Fg");
        var mask_1_graphics_234 = new cjs.Graphics().p("EgfRA8DMAAAh4FMBliAAAMAAAB4Fg");
        var mask_1_graphics_235 = new cjs.Graphics().p("EhQAA8DMAAAh4FMCgBAAAMAAAB4Fg");
        var mask_1_graphics_271 = new cjs.Graphics().p("EhQAA8DMAAAh4FMCgBAAAMAAAB4Fg");
        var mask_1_graphics_272 = new cjs.Graphics().p("Eg8AA8DMAAAh4FMCIAAAAMAAAB4Fg");
        var mask_1_graphics_273 = new cjs.Graphics().p("EgoAA8DMAAAh4FMBwAAAAMAAAB4Fg");
        var mask_1_graphics_274 = new cjs.Graphics().p("EgUAA8DMAAAh4FMBYAAAAMAAAB4Fg");
        var mask_1_graphics_275 = new cjs.Graphics().p("EAAAA8DMAAAh4FMBAAAAAMAAAB4Fg");
        var mask_1_graphics_276 = new cjs.Graphics().p("EAUAA8DMAAAh4FMAoAAAAMAAAB4Fg");
        var mask_1_graphics_308 = new cjs.Graphics().p("EhQAA8DMAAAh4FMCgBAAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask_1).to({ graphics: null, x: 0, y: 0 }).wait(209).to({ graphics: mask_1_graphics_209, x: 384.024, y: -384.325 }).wait(1).to({ graphics: mask_1_graphics_210, x: 384.024, y: -325.275 }).wait(1).to({ graphics: mask_1_graphics_211, x: 384.024, y: -266.175 }).wait(1).to({ graphics: mask_1_graphics_212, x: 384.024, y: -207.075 }).wait(1).to({ graphics: mask_1_graphics_213, x: 384.024, y: -148.025 }).wait(1).to({ graphics: mask_1_graphics_214, x: 384.024, y: -88.925 }).wait(1).to({ graphics: mask_1_graphics_215, x: 384.024, y: -29.875 }).wait(1).to({ graphics: mask_1_graphics_216, x: 384.024, y: 29.225 }).wait(1).to({ graphics: mask_1_graphics_217, x: 384.024, y: 88.275 }).wait(1).to({ graphics: mask_1_graphics_218, x: 384.024, y: 147.375 }).wait(1).to({ graphics: mask_1_graphics_219, x: 384.024, y: 206.425 }).wait(1).to({ graphics: mask_1_graphics_220, x: 384.024, y: 265.525 }).wait(1).to({ graphics: mask_1_graphics_221, x: 384.024, y: 324.625 }).wait(1).to({ graphics: mask_1_graphics_222, x: 384.024, y: 383.675 }).wait(5).to({ graphics: mask_1_graphics_227, x: 384.024, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_228, x: 384.0299, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_229, x: 384.1615, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_230, x: 384.9699, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_231, x: 388.0211, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_232, x: 396.2096, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_233, x: 414.4174, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_234, x: 449.7422, y: 383.725 }).wait(1).to({ graphics: mask_1_graphics_235, x: 511.9, y: 383.675 }).wait(36).to({ graphics: mask_1_graphics_271, x: 511.9, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_272, x: 486.373, y: 383.675 }).wait(1).to({ graphics: mask_1_graphics_273, x: 460.7648, y: 383.625 }).wait(1).to({ graphics: mask_1_graphics_274, x: 435.1537, y: 383.625 }).wait(1).to({ graphics: mask_1_graphics_275, x: 409.5514, y: 383.575 }).wait(1).to({ graphics: mask_1_graphics_276, x: 384.024, y: 383.675 }).wait(32).to({ graphics: mask_1_graphics_308, x: 511.9, y: 383.675 }).wait(192));

        // cr_tint
        this.instance_5 = new lib.cr_tint();
        this.instance_5.parent = this;
        this.instance_5.setTransform(512, 384.2, 1, 1, 0, 0, 0, 512, 384.6);
        this.instance_5.alpha = 0.5;
        this.instance_5._off = true;

        var maskedShapeInstanceList = [this.instance_5];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_5).wait(209).to({ _off: false }, 0).wait(20).to({ alpha: 0 }, 5).wait(37).to({ alpha: 0.5 }, 5).wait(32).to({ alpha: 0 }, 0).to({ _off: true }, 1).wait(191));

        // cr_bkg
        this.instance_6 = new lib.cr_bkg();
        this.instance_6.parent = this;
        this.instance_6.setTransform(2759.55, 1899.75, 4.1643, 4.1642, 0, 0, 0, 709.5, 472.3);
        this.instance_6._off = true;

        this.instance_7 = new lib.CR_bkg();
        this.instance_7.parent = this;
        this.instance_7.setTransform(-395, -67, 0.3335, 0.3335);

        var maskedShapeInstanceList = [this.instance_6, this.instance_7];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_1;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [] }).to({ state: [{ t: this.instance_6 }] }, 209).to({ state: [{ t: this.instance_6 }] }, 20).to({ state: [{ t: this.instance_6 }] }, 5).to({ state: [{ t: this.instance_6 }] }, 37).to({ state: [{ t: this.instance_6 }] }, 5).to({ state: [{ t: this.instance_7 }] }, 32).to({ state: [] }, 1).wait(191));
        this.timeline.addTween(cjs.Tween.get(this.instance_6).wait(209).to({ _off: false }, 0).wait(20).to({ x: 2559.55 }, 5).wait(37).to({ x: 2759.55 }, 5).to({ _off: true }, 32).wait(192));

        // im_blue_top (mask)
        var mask_2 = new cjs.Shape();
        mask_2._off = true;
        var mask_2_graphics_309 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_310 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_311 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_312 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_313 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_314 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_315 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_316 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_317 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_318 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_319 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_320 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_321 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_322 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_327 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");
        var mask_2_graphics_328 = new cjs.Graphics().p("EAanA8DMAAAh4FMA1nAAAMAAAB4Fg");
        var mask_2_graphics_329 = new cjs.Graphics().p("EANSA8DMAAAh4FMBC7AAAMAAAB4Fg");
        var mask_2_graphics_330 = new cjs.Graphics().p("EgABA8DMAAAh4FMBQMAAAMAAAB4Fg");
        var mask_2_graphics_331 = new cjs.Graphics().p("EgNVA8DMAAAh4FMBdfAAAMAAAB4Fg");
        var mask_2_graphics_332 = new cjs.Graphics().p("EgaqA8DMAAAh4FMBqyAAAMAAAB4Fg");
        var mask_2_graphics_333 = new cjs.Graphics().p("Egn+A8DMAAAh4FMB4FAAAMAAAB4Fg");
        var mask_2_graphics_334 = new cjs.Graphics().p("Eg1SA8DMAAAh4FMCFYAAAMAAAB4Fg");
        var mask_2_graphics_335 = new cjs.Graphics().p("EhCnA8DMAAAh4FMCSsAAAMAAAB4Fg");
        var mask_2_graphics_336 = new cjs.Graphics().p("EhP9A8DMAAAh4FMCf/AAAMAAAB4Fg");
        var mask_2_graphics_374 = new cjs.Graphics().p("EhP9A8DMAAAh4FMCf/AAAMAAAB4Fg");
        var mask_2_graphics_375 = new cjs.Graphics().p("Eg7+A8DMAAAh4FMCMDAAAMAAAB4Fg");
        var mask_2_graphics_376 = new cjs.Graphics().p("Egn/A8DMAAAh4FMB4FAAAMAAAB4Fg");
        var mask_2_graphics_377 = new cjs.Graphics().p("EgUAA8DMAAAh4FMBkJAAAMAAAB4Fg");
        var mask_2_graphics_378 = new cjs.Graphics().p("EgACA8DMAAAh4FMBQNAAAMAAAB4Fg");
        var mask_2_graphics_379 = new cjs.Graphics().p("EAT8A8DMAAAh4FMA8QAAAMAAAB4Fg");
        var mask_2_graphics_380 = new cjs.Graphics().p("EAn7A8DMAAAh4FMAoUAAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask_2).to({ graphics: null, x: 0, y: 0 }).wait(309).to({ graphics: mask_2_graphics_309, x: 513.5039, y: -384.325 }).wait(1).to({ graphics: mask_2_graphics_310, x: 513.5, y: -325.225 }).wait(1).to({ graphics: mask_2_graphics_311, x: 513.5, y: -266.075 }).wait(1).to({ graphics: mask_2_graphics_312, x: 513.5, y: -206.925 }).wait(1).to({ graphics: mask_2_graphics_313, x: 513.5, y: -147.825 }).wait(1).to({ graphics: mask_2_graphics_314, x: 513.5, y: -88.675 }).wait(1).to({ graphics: mask_2_graphics_315, x: 513.5, y: -29.575 }).wait(1).to({ graphics: mask_2_graphics_316, x: 513.5, y: 29.575 }).wait(1).to({ graphics: mask_2_graphics_317, x: 513.5, y: 88.675 }).wait(1).to({ graphics: mask_2_graphics_318, x: 513.5, y: 147.825 }).wait(1).to({ graphics: mask_2_graphics_319, x: 513.5, y: 206.925 }).wait(1).to({ graphics: mask_2_graphics_320, x: 513.5, y: 266.075 }).wait(1).to({ graphics: mask_2_graphics_321, x: 513.5, y: 325.225 }).wait(1).to({ graphics: mask_2_graphics_322, x: 513.5039, y: 384.325 }).wait(5).to({ graphics: mask_2_graphics_327, x: 513.5039, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_328, x: 513.3773, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_329, x: 513.2586, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_330, x: 513.1109, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_331, x: 513.0133, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_332, x: 512.8445, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_333, x: 512.7219, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_334, x: 512.6242, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_335, x: 512.4805, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_336, x: 512.2367, y: 384.325 }).wait(38).to({ graphics: mask_2_graphics_374, x: 512.2367, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_375, x: 512.4648, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_376, x: 512.6469, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_377, x: 512.8539, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_378, x: 513.0609, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_379, x: 513.243, y: 384.325 }).wait(1).to({ graphics: mask_2_graphics_380, x: 513.5039, y: 384.325 }).wait(120));

        // im_tint
        this.instance_8 = new lib.im_tint();
        this.instance_8.parent = this;
        this.instance_8.setTransform(512, 384.2, 1, 1, 0, 0, 0, 512, 384.6);
        this.instance_8.alpha = 0.5;
        this.instance_8._off = true;

        var maskedShapeInstanceList = [this.instance_8];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_2;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_8).wait(309).to({ _off: false }, 0).wait(20).to({ alpha: 0 }, 6).wait(39).to({ alpha: 0.5 }, 5).to({ _off: true }, 31).wait(90));

        // im_bkg
        this.instance_9 = new lib.IMAAC_bkg();
        this.instance_9.parent = this;
        this.instance_9.setTransform(-401, 0, 1.6969, 1.6971);
        this.instance_9._off = true;

        var maskedShapeInstanceList = [this.instance_9];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_2;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_9).wait(309).to({ _off: false }, 0).to({ _off: true }, 101).wait(90));

        // cbrb_log
        this.instance_10 = new lib.login_button2();
        this.instance_10.parent = this;
        this.instance_10.setTransform(778.55, 829.45);
        this.instance_10.alpha = 0.3984;
        this.instance_10._off = true;
        new cjs.ButtonHelper(this.instance_10, 0, 1, 2, false, new lib.login_button2(), 3);

        this.log_button = new lib.login_button2();
        this.log_button.name = "log_button";
        this.log_button.parent = this;
        this.log_button.setTransform(778.55, 546.45);
        this.log_button._off = true;
        new cjs.ButtonHelper(this.log_button, 0, 1, 2, false, new lib.login_button2(), 3);

        this.timeline.addTween(cjs.Tween.get(this.instance_10).wait(414).to({ _off: false }, 0).to({ _off: true, y: 546.45, alpha: 1 }, 6).wait(80));
        this.timeline.addTween(cjs.Tween.get(this.log_button).wait(414).to({ _off: false }, 6).wait(8).to({ y: 624.45 }, 0).to({ y: 829.45, alpha: 0.3984 }, 6).to({ _off: true }, 4).wait(62));

        // cbrb_reg
        this.instance_11 = new lib.reg_button2();
        this.instance_11.parent = this;
        this.instance_11.setTransform(778.55, 832.1);
        this.instance_11.alpha = 0.3984;
        this.instance_11._off = true;
        new cjs.ButtonHelper(this.instance_11, 0, 1, 2, false, new lib.reg_button2(), 3);

        this.reg_button = new lib.reg_button2();
        this.reg_button.name = "reg_button";
        this.reg_button.parent = this;
        this.reg_button.setTransform(778.55, 446.6);
        this.reg_button._off = true;
        new cjs.ButtonHelper(this.reg_button, 0, 1, 2, false, new lib.reg_button2(), 3);

        this.timeline.addTween(cjs.Tween.get(this.instance_11).wait(412).to({ _off: false }, 0).to({ _off: true, y: 446.6, alpha: 1 }, 6).wait(82));
        this.timeline.addTween(cjs.Tween.get(this.reg_button).wait(412).to({ _off: false }, 6).wait(12).to({ y: 524.6 }, 0).to({ y: 832.1, alpha: 0.3984 }, 6).to({ _off: true }, 3).wait(61));

        // cbrn_pill_top
        this.cbrn_pill_top = new lib.cbrn_pill();
        this.cbrn_pill_top.name = "cbrn_pill_top";
        this.cbrn_pill_top.parent = this;
        this.cbrn_pill_top.setTransform(512, 434.1, 0.5237, 0.5237, 0, 0, 0, 0, 0.1);
        this.cbrn_pill_top._off = true;
        new cjs.ButtonHelper(this.cbrn_pill_top, 0, 1, 1);

        this.timeline.addTween(cjs.Tween.get(this.cbrn_pill_top).wait(410).to({ _off: false }, 0).wait(3).to({ scaleX: 0.3696, scaleY: 0.3696, x: 322.15, y: 353.95 }, 3).wait(16).to({ scaleX: 0.5237, scaleY: 0.5237, x: 512, y: 434.1 }, 3).to({ _off: true }, 14).wait(51));

        // cbrn_text
        this.cbrn_text = new lib.cbrn_test();
        this.cbrn_text.name = "cbrn_text";
        this.cbrn_text.parent = this;
        this.cbrn_text.setTransform(340.5, 297.1, 1, 1, 0, 0, 0, 0, -286);
        this.cbrn_text.alpha = 0;
        this.cbrn_text._off = true;
        new cjs.ButtonHelper(this.cbrn_text, 0, 1, 1);

        this.timeline.addTween(cjs.Tween.get(this.cbrn_text).wait(414).to({ _off: false }, 0).to({ regY: -260.6, y: 306.1, alpha: 1 }, 3).wait(14).to({ regY: -254.8, y: 311.9 }, 0).to({ regY: -288, y: 295.1, alpha: 0 }, 3).wait(66));

        // cbrn_blue
        this.instance_12 = new lib.cbrn_blue();
        this.instance_12.parent = this;
        this.instance_12.setTransform(511.55, 996.15);
        this.instance_12._off = true;

        this.timeline.addTween(cjs.Tween.get(this.instance_12).wait(410).to({ _off: false }, 0).to({ regY: 220.6, scaleY: 1.0326, y: 768.75 }, 6, cjs.Ease.quintInOut).wait(16).to({ regY: 0, scaleY: 1, y: 996.15 }, 6).wait(62));

        // rr_green (mask)
        var mask_3 = new cjs.Shape();
        mask_3._off = true;
        var mask_3_graphics_0 = new cjs.Graphics().p("EgT/A8DMAAAh4FMAn/AAAMAAAB4Fg");
        var mask_3_graphics_27 = new cjs.Graphics().p("EgT+A8DMAAAh4FMAn+AAAMAAAB4Fg");
        var mask_3_graphics_28 = new cjs.Graphics().p("EgbeA8DMAAAh4FMA2+AAAMAAAB4Fg");
        var mask_3_graphics_29 = new cjs.Graphics().p("Egi+A8DMAAAh4FMBF+AAAMAAAB4Fg");
        var mask_3_graphics_30 = new cjs.Graphics().p("EgqeA8DMAAAh4FMBU+AAAMAAAB4Fg");
        var mask_3_graphics_31 = new cjs.Graphics().p("Egx+A8DMAAAh4FMBj/AAAMAAAB4Fg");
        var mask_3_graphics_32 = new cjs.Graphics().p("Eg5eA8DMAAAh4FMBy/AAAMAAAB4Fg");
        var mask_3_graphics_33 = new cjs.Graphics().p("EhA+A8DMAAAh4FMCB/AAAMAAAB4Fg");
        var mask_3_graphics_34 = new cjs.Graphics().p("EhIdA8DMAAAh4FMCQ+AAAMAAAB4Fg");
        var mask_3_graphics_35 = new cjs.Graphics().p("EhP/A8DMAAAh4FMCf/AAAMAAAB4Fg");
        var mask_3_graphics_70 = new cjs.Graphics().p("EhP/A8DMAAAh4FMCf/AAAMAAAB4Fg");
        var mask_3_graphics_71 = new cjs.Graphics().p("EhJUA8DMAAAh4FMCSpAAAMAAAB4Fg");
        var mask_3_graphics_72 = new cjs.Graphics().p("EhCpA8DMAAAh4FMCFTAAAMAAAB4Fg");
        var mask_3_graphics_73 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_3_graphics_74 = new cjs.Graphics().p("Eg1UA8DMAAAh4FMBqpAAAMAAAB4Fg");
        var mask_3_graphics_75 = new cjs.Graphics().p("EgupA8EMAAAh4GMBdTAAAMAAAB4Gg");
        var mask_3_graphics_76 = new cjs.Graphics().p("Egn/A8EMAAAh4GMBP/AAAMAAAB4Gg");
        var mask_3_graphics_77 = new cjs.Graphics().p("EghUA8EMAAAh4GMBCpAAAMAAAB4Gg");
        var mask_3_graphics_78 = new cjs.Graphics().p("EgapA8EMAAAh4FMA1TAAAMAAAB4Fg");
        var mask_3_graphics_79 = new cjs.Graphics().p("EgT+A8DMAAAh4FMAn+AAAMAAAB4Fg");
        var mask_3_graphics_109 = new cjs.Graphics().p("EgT/A8DMAAAh4FMAn/AAAMAAAB4Fg");
        var mask_3_graphics_209 = new cjs.Graphics().p("EgT/A8DMAAAh4FMAn/AAAMAAAB4Fg");
        var mask_3_graphics_309 = new cjs.Graphics().p("EgT/A8DMAAAh4FMAn/AAAMAAAB4Fg");
        var mask_3_graphics_409 = new cjs.Graphics().p("EgT/A8DMAAAh4FMAn/AAAMAAAB4Fg");
        var mask_3_graphics_410 = new cjs.Graphics().p("EgT/A8DMAAAh4FMAn/AAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask_3).to({ graphics: mask_3_graphics_0, x: 128, y: 384.325 }).wait(27).to({ graphics: mask_3_graphics_27, x: 127.975, y: 384.325 }).wait(1).to({ graphics: mask_3_graphics_28, x: 176.0023, y: 384.325 }).wait(1).to({ graphics: mask_3_graphics_29, x: 224.0047, y: 384.275 }).wait(1).to({ graphics: mask_3_graphics_30, x: 272.031, y: 384.275 }).wait(1).to({ graphics: mask_3_graphics_31, x: 320.0584, y: 384.275 }).wait(1).to({ graphics: mask_3_graphics_32, x: 368.0857, y: 384.225 }).wait(1).to({ graphics: mask_3_graphics_33, x: 416.0881, y: 384.225 }).wait(1).to({ graphics: mask_3_graphics_34, x: 464.1144, y: 384.175 }).wait(1).to({ graphics: mask_3_graphics_35, x: 511.9, y: 384.325 }).wait(35).to({ graphics: mask_3_graphics_70, x: 511.9, y: 384.325 }).wait(1).to({ graphics: mask_3_graphics_71, x: 469.3, y: 384.325 }).wait(1).to({ graphics: mask_3_graphics_72, x: 426.642, y: 384.35 }).wait(1).to({ graphics: mask_3_graphics_73, x: 383.95, y: 384.35 }).wait(1).to({ graphics: mask_3_graphics_74, x: 341.2962, y: 384.35 }).wait(1).to({ graphics: mask_3_graphics_75, x: 298.6, y: 384.375 }).wait(1).to({ graphics: mask_3_graphics_76, x: 255.95, y: 384.375 }).wait(1).to({ graphics: mask_3_graphics_77, x: 213.2948, y: 384.375 }).wait(1).to({ graphics: mask_3_graphics_78, x: 170.6, y: 384.4 }).wait(1).to({ graphics: mask_3_graphics_79, x: 127.975, y: 384.325 }).wait(30).to({ graphics: mask_3_graphics_109, x: 128, y: 384.325 }).wait(100).to({ graphics: mask_3_graphics_209, x: 128, y: 384.325 }).wait(100).to({ graphics: mask_3_graphics_309, x: 128, y: 384.325 }).wait(100).to({ graphics: mask_3_graphics_409, x: 128, y: 384.325 }).wait(1).to({ graphics: mask_3_graphics_410, x: 128, y: 384.325 }).wait(90));

        // rr_green_tint
        this.instance_13 = new lib.green_tint();
        this.instance_13.parent = this;
        this.instance_13.setTransform(512, 384.15, 1, 1, 0, 0, 0, 512, 384.5);

        var maskedShapeInstanceList = [this.instance_13];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_3;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_13).wait(27).to({ alpha: 0 }, 8).wait(35).to({ alpha: 1 }, 9).wait(421));

        // rr_image
        this.instance_14 = new lib.rr_image();
        this.instance_14.parent = this;
        this.instance_14.setTransform(1871.55, 1692.45, 4.1664, 4.1663, 0, 0, 0, 532.6, 422.3);

        var maskedShapeInstanceList = [this.instance_14];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_3;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_14).wait(33).to({ x: 2218.95 }, 4).wait(33).to({ x: 1871.55 }, 9).wait(421));

        // cbrn_pill
        this.cbrn_pill = new lib.cbrn_pill();
        this.cbrn_pill.name = "cbrn_pill";
        this.cbrn_pill.parent = this;
        this.cbrn_pill.setTransform(512, 434.1, 0.5237, 0.5237, 0, 0, 0, 0, 0.1);
        new cjs.ButtonHelper(this.cbrn_pill, 0, 1, 1);

        this.timeline.addTween(cjs.Tween.get(this.cbrn_pill).to({ _off: true }, 35).wait(38).to({ _off: false }, 0).to({ _off: true }, 61).wait(75).to({ _off: false }, 0).to({ _off: true }, 25).wait(75).to({ _off: false }, 0).to({ _off: true }, 24).wait(116).to({ _off: false, x: 19 }, 0).to({ x: 512 }, 6).wait(45));

        // br_red (mask)
        var mask_4 = new cjs.Shape();
        mask_4._off = true;
        var mask_4_graphics_0 = new cjs.Graphics().p("Egn/A8DMAAAh4FMBP/AAAMAAAB4Fg");
        var mask_4_graphics_109 = new cjs.Graphics().p("Egn/A8DMAAAh4FMBP/AAAMAAAB4Fg");
        var mask_4_graphics_209 = new cjs.Graphics().p("Egn/A8DMAAAh4FMBP/AAAMAAAB4Fg");
        var mask_4_graphics_309 = new cjs.Graphics().p("Egn/A8DMAAAh4FMBP/AAAMAAAB4Fg");
        var mask_4_graphics_409 = new cjs.Graphics().p("Egn/A8DMAAAh4FMBP/AAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask_4).to({ graphics: mask_4_graphics_0, x: 256.025, y: 384.325 }).wait(109).to({ graphics: mask_4_graphics_109, x: 256.025, y: 384.325 }).wait(100).to({ graphics: mask_4_graphics_209, x: 256.025, y: 384.325 }).wait(100).to({ graphics: mask_4_graphics_309, x: 256.025, y: 384.325 }).wait(100).to({ graphics: mask_4_graphics_409, x: 256.025, y: 384.325 }).wait(91));

        // br_tint
        this.instance_15 = new lib.br_tint();
        this.instance_15.parent = this;
        this.instance_15.setTransform(512, 384.5, 1, 1, 0, 0, 0, 512, 384.5);

        var maskedShapeInstanceList = [this.instance_15];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_4;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_15).wait(500));

        // br_bkg
        this.instance_16 = new lib.BR_bkg();
        this.instance_16.parent = this;
        this.instance_16.setTransform(0, 0, 0.7986, 0.7988);

        var maskedShapeInstanceList = [this.instance_16];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_4;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_16).wait(500));

        // cr_orange (mask)
        var mask_5 = new cjs.Shape();
        mask_5._off = true;
        var mask_5_graphics_0 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_5_graphics_109 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_5_graphics_209 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_5_graphics_309 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_5_graphics_409 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");
        var mask_5_graphics_410 = new cjs.Graphics().p("Eg7/A8DMAAAh4FMB3/AAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask_5).to({ graphics: mask_5_graphics_0, x: 384, y: 384.325 }).wait(109).to({ graphics: mask_5_graphics_109, x: 384, y: 384.325 }).wait(100).to({ graphics: mask_5_graphics_209, x: 384, y: 384.325 }).wait(100).to({ graphics: mask_5_graphics_309, x: 384, y: 384.325 }).wait(100).to({ graphics: mask_5_graphics_409, x: 384, y: 384.325 }).wait(1).to({ graphics: mask_5_graphics_410, x: 384, y: 384.325 }).wait(90));

        // cr_tint
        this.instance_17 = new lib.cr_tint();
        this.instance_17.parent = this;
        this.instance_17.setTransform(512, 384.2, 1, 1, 0, 0, 0, 512, 384.6);
        this.instance_17.alpha = 0.5;

        var maskedShapeInstanceList = [this.instance_17];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_5;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_17).wait(500));

        // cr_bkg
        this.instance_18 = new lib.CR_bkg();
        this.instance_18.parent = this;
        this.instance_18.setTransform(-195, -67, 1.3888, 1.3889);

        var maskedShapeInstanceList = [this.instance_18];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_5;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_18).wait(500));

        // im_blue (mask)
        var mask_6 = new cjs.Shape();
        mask_6._off = true;
        var mask_6_graphics_0 = new cjs.Graphics().p("EhP+A8DMAAAh4FMCf+AAAMAAAB4Fg");
        var mask_6_graphics_109 = new cjs.Graphics().p("EhP+A8DMAAAh4FMCf+AAAMAAAB4Fg");
        var mask_6_graphics_209 = new cjs.Graphics().p("EhP+A8DMAAAh4FMCf+AAAMAAAB4Fg");
        var mask_6_graphics_309 = new cjs.Graphics().p("EhP+A8DMAAAh4FMCf+AAAMAAAB4Fg");
        var mask_6_graphics_409 = new cjs.Graphics().p("EhP+A8DMAAAh4FMCf+AAAMAAAB4Fg");
        var mask_6_graphics_410 = new cjs.Graphics().p("EhP+A8DMAAAh4FMCf+AAAMAAAB4Fg");

        this.timeline.addTween(cjs.Tween.get(mask_6).to({ graphics: mask_6_graphics_0, x: 512, y: 384.325 }).wait(109).to({ graphics: mask_6_graphics_109, x: 512, y: 384.325 }).wait(100).to({ graphics: mask_6_graphics_209, x: 512, y: 384.325 }).wait(100).to({ graphics: mask_6_graphics_309, x: 512, y: 384.325 }).wait(100).to({ graphics: mask_6_graphics_409, x: 512, y: 384.325 }).wait(1).to({ graphics: mask_6_graphics_410, x: 512, y: 384.325 }).wait(90));

        // im_tint
        this.instance_19 = new lib.im_tint();
        this.instance_19.parent = this;
        this.instance_19.setTransform(512, 384.2, 1, 1, 0, 0, 0, 512, 384.6);
        this.instance_19.alpha = 0.5;

        this.shape_1 = new cjs.Shape();
        this.shape_1.graphics.f().s("#000000").ss(1, 1, 1).p("EhP/g8FMCf/AAAMAAAB4LMif/AAAg");
        this.shape_1.setTransform(512, 384.15);

        var maskedShapeInstanceList = [this.instance_19, this.shape_1];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_6;
        }

        this.timeline.addTween(cjs.Tween.get({}).to({ state: [{ t: this.shape_1 }, { t: this.instance_19 }] }).to({ state: [{ t: this.shape_1 }, { t: this.instance_19 }] }, 410).wait(90));

        // im_bkg
        this.instance_20 = new lib.IMAAC_bkg();
        this.instance_20.parent = this;
        this.instance_20.setTransform(-401, 0, 1.6969, 1.6971);

        var maskedShapeInstanceList = [this.instance_20];

        for (var shapedInstanceItr = 0; shapedInstanceItr < maskedShapeInstanceList.length; shapedInstanceItr++) {
            maskedShapeInstanceList[shapedInstanceItr].mask = mask_6;
        }

        this.timeline.addTween(cjs.Tween.get(this.instance_20).wait(500));

    }).prototype = p = new cjs.MovieClip();
    p.nominalBounds = new cjs.Rectangle(129.3, 193.7, 965.3, 1023.0999999999999);
    // library properties:
    lib.properties = {
        id: 'F39C5262F2C66D44A33E6756DA740E84',
        width: 1024,
        height: 768,
        fps: 24,
        color: "#FFFFFF",
        opacity: 1.00,
        manifest: [
            { src: RadResponder.getResourceAddress() + "images/custom/animation/cbrn-preauth-homepage/cbrnSplash_atlas_.png", id: "cbrnSplash_atlas_" }
        ],
        preloads: []
    };



    // bootstrap callback support:

    (lib.Stage = function (canvas) {
        createjs.Stage.call(this, canvas);
    }).prototype = p = new createjs.Stage();

    p.setAutoPlay = function (autoPlay) {
        this.tickEnabled = autoPlay;
    }
    p.play = function () { this.tickEnabled = true; this.getChildAt(0).gotoAndPlay(this.getTimelinePosition()) }
    p.stop = function (ms) { if (ms) this.seek(ms); this.tickEnabled = false; }
    p.seek = function (ms) { this.tickEnabled = true; this.getChildAt(0).gotoAndStop(lib.properties.fps * ms / 1000); }
    p.getDuration = function () { return this.getChildAt(0).totalFrames / lib.properties.fps * 1000; }

    p.getTimelinePosition = function () { return this.getChildAt(0).currentFrame / lib.properties.fps * 1000; }

    an.bootcompsLoaded = an.bootcompsLoaded || [];
    if (!an.bootstrapListeners) {
        an.bootstrapListeners = [];
    }

    an.bootstrapCallback = function (fnCallback) {
        an.bootstrapListeners.push(fnCallback);
        if (an.bootcompsLoaded.length > 0) {
            for (var i = 0; i < an.bootcompsLoaded.length; ++i) {
                fnCallback(an.bootcompsLoaded[i]);
            }
        }
    };

    an.compositions = an.compositions || {};
    an.compositions['F39C5262F2C66D44A33E6756DA740E84'] = {
        getStage: function () { return exportRoot.getStage(); },
        getLibrary: function () { return lib; },
        getSpriteSheet: function () { return ss; },
        getImages: function () { return img; }
    };

    an.compositionLoaded = function (id) {
        an.bootcompsLoaded.push(id);
        for (var j = 0; j < an.bootstrapListeners.length; j++) {
            an.bootstrapListeners[j](id);
        }
    }

    an.getComposition = function (id) {
        return an.compositions[id];
    }


    an.makeResponsive = function (isResp, respDim, isScale, scaleType, domContainers) {
        var lastW, lastH, lastS = 1;
        window.addEventListener('resize', resizeCanvas);
        resizeCanvas();
        function resizeCanvas() {
            var w = lib.properties.width, h = lib.properties.height;
            var iw = window.innerWidth, ih = window.innerHeight;
            var cw = document.documentElement.clientWidth, ch = document.documentElement.clientHeight;
            var pRatio = window.devicePixelRatio || 1, xRatio = cw / w, yRatio = ch / h, sRatio = 1;
            if (isResp) {
                if ((respDim == 'width' && lastW == iw) || (respDim == 'height' && lastH == ih)) {
                    //sRatio = lastS;                
                }
                else if (!isScale) {
                    if (iw < w || ih < h)
                        sRatio = Math.min(xRatio, yRatio);
                }
                else if (iw < 1024) {
                    //console.log('less ' + iw);
                    sRatio = Math.min(xRatio, yRatio);
                }
                else if (iw > 1024 || iw > lastW) {
                    //console.log('more ' + iw);			
                    sRatio = Math.max(xRatio, yRatio);
                }
            }
            domContainers[0].width = w * pRatio * sRatio;
            domContainers[0].height = h * pRatio * sRatio;
            domContainers.forEach(function (container) {
                container.style.width = w * sRatio + 'px';
                container.style.height = h * sRatio + 'px';
            });
            stage.scaleX = pRatio * sRatio;
            stage.scaleY = pRatio * sRatio;
            lastW = iw; lastH = ih; lastS = sRatio;
            stage.tickOnUpdate = false;
            stage.update();
            stage.tickOnUpdate = true;
        }
    }

    init();

});;
'use strict';

/**
 * Copyright Marc J. Schmidt. See the LICENSE file at the top-level
 * directory of this distribution and at
 * https://github.com/marcj/css-element-queries/blob/master/LICENSE.
 */
(function (root, factory) {
    if (typeof define === "function" && define.amd) {
        define(factory);
    } else if (typeof exports === "object") {
        module.exports = factory();
    } else {
        root.ResizeSensor = factory();
    }
}(typeof window !== 'undefined' ? window : this, function () {

    // Make sure it does not throw in a SSR (Server Side Rendering) situation
    if (typeof window === "undefined") {
        return null;
    }
    // https://github.com/Semantic-Org/Semantic-UI/issues/3855
    // https://github.com/marcj/css-element-queries/issues/257
    var globalWindow = typeof window != 'undefined' && window.Math == Math
        ? window
        : typeof self != 'undefined' && self.Math == Math
            ? self
            : Function('return this')();
    // Only used for the dirty checking, so the event callback count is limited to max 1 call per fps per sensor.
    // In combination with the event based resize sensor this saves cpu time, because the sensor is too fast and
    // would generate too many unnecessary events.
    var requestAnimationFrame = globalWindow.requestAnimationFrame ||
        globalWindow.mozRequestAnimationFrame ||
        globalWindow.webkitRequestAnimationFrame ||
        function (fn) {
            return globalWindow.setTimeout(fn, 20);
        };

    /**
     * Iterate over each of the provided element(s).
     *
     * @param {HTMLElement|HTMLElement[]} elements
     * @param {Function}                  callback
     */
    function forEachElement(elements, callback){
        var elementsType = Object.prototype.toString.call(elements);
        var isCollectionTyped = ('[object Array]' === elementsType
            || ('[object NodeList]' === elementsType)
            || ('[object HTMLCollection]' === elementsType)
            || ('[object Object]' === elementsType)
            || ('undefined' !== typeof jQuery && elements instanceof jQuery) //jquery
            || ('undefined' !== typeof Elements && elements instanceof Elements) //mootools
        );
        var i = 0, j = elements.length;
        if (isCollectionTyped) {
            for (; i < j; i++) {
                callback(elements[i]);
            }
        } else {
            callback(elements);
        }
    }

    /**
    * Get element size
    * @param {HTMLElement} element
    * @returns {Object} {width, height}
    */
    function getElementSize(element) {
        if (!element.getBoundingClientRect) {
            return {
                width: element.offsetWidth,
                height: element.offsetHeight
            }
        }

        var rect = element.getBoundingClientRect();
        return {
            width: Math.round(rect.width),
            height: Math.round(rect.height)
        }
    }

    /**
     * Apply CSS styles to element.
     *
     * @param {HTMLElement} element
     * @param {Object} style
     */
    function setStyle(element, style) {
        Object.keys(style).forEach(function(key) {
            element.style[key] = style[key];
        });
    }

    /**
     * Class for dimension change detection.
     *
     * @param {Element|Element[]|Elements|jQuery} element
     * @param {Function} callback
     *
     * @constructor
     */
    var ResizeSensor = function(element, callback) {
        /**
         *
         * @constructor
         */
        function EventQueue() {
            var q = [];
            this.add = function(ev) {
                q.push(ev);
            };

            var i, j;
            this.call = function(sizeInfo) {
                for (i = 0, j = q.length; i < j; i++) {
                    q[i].call(this, sizeInfo);
                }
            };

            this.remove = function(ev) {
                var newQueue = [];
                for(i = 0, j = q.length; i < j; i++) {
                    if(q[i] !== ev) newQueue.push(q[i]);
                }
                q = newQueue;
            };

            this.length = function() {
                return q.length;
            }
        }

        /**
         *
         * @param {HTMLElement} element
         * @param {Function}    resized
         */
        function attachResizeEvent(element, resized) {
            if (!element) return;
            if (element.resizedAttached) {
                element.resizedAttached.add(resized);
                return;
            }

            element.resizedAttached = new EventQueue();
            element.resizedAttached.add(resized);

            element.resizeSensor = document.createElement('div');
            element.resizeSensor.dir = 'ltr';
            element.resizeSensor.className = 'resize-sensor';

            var style = {
                pointerEvents: 'none',
                position: 'absolute',
                left: '0px',
                top: '0px',
                right: '0px',
                bottom: '0px',
                overflow: 'hidden',
                zIndex: '-1',
                visibility: 'hidden',
                maxWidth: '100%'
            };
            var styleChild = {
                position: 'absolute',
                left: '0px',
                top: '0px',
                transition: '0s',
            };

            setStyle(element.resizeSensor, style);

            var expand = document.createElement('div');
            expand.className = 'resize-sensor-expand';
            setStyle(expand, style);

            var expandChild = document.createElement('div');
            setStyle(expandChild, styleChild);
            expand.appendChild(expandChild);

            var shrink = document.createElement('div');
            shrink.className = 'resize-sensor-shrink';
            setStyle(shrink, style);

            var shrinkChild = document.createElement('div');
            setStyle(shrinkChild, styleChild);
            setStyle(shrinkChild, { width: '200%', height: '200%' });
            shrink.appendChild(shrinkChild);

            element.resizeSensor.appendChild(expand);
            element.resizeSensor.appendChild(shrink);
            element.appendChild(element.resizeSensor);

            var computedStyle = window.getComputedStyle(element);
            var position = computedStyle ? computedStyle.getPropertyValue('position') : null;
            if ('absolute' !== position && 'relative' !== position && 'fixed' !== position) {
                element.style.position = 'relative';
            }

            var dirty, rafId;
            var size = getElementSize(element);
            var lastWidth = 0;
            var lastHeight = 0;
            var initialHiddenCheck = true;
            var lastAnimationFrame = 0;

            var resetExpandShrink = function () {
                var width = element.offsetWidth;
                var height = element.offsetHeight;

                expandChild.style.width = (width + 10) + 'px';
                expandChild.style.height = (height + 10) + 'px';

                expand.scrollLeft = width + 10;
                expand.scrollTop = height + 10;

                shrink.scrollLeft = width + 10;
                shrink.scrollTop = height + 10;
            };

            var reset = function() {
                // Check if element is hidden
                if (initialHiddenCheck) {
                    var invisible = element.offsetWidth === 0 && element.offsetHeight === 0;
                    if (invisible) {
                        // Check in next frame
                        if (!lastAnimationFrame){
                            lastAnimationFrame = requestAnimationFrame(function(){
                                lastAnimationFrame = 0;

                                reset();
                            });
                        }

                        return;
                    } else {
                        // Stop checking
                        initialHiddenCheck = false;
                    }
                }

                resetExpandShrink();
            };
            element.resizeSensor.resetSensor = reset;

            var onResized = function() {
                rafId = 0;

                if (!dirty) return;

                lastWidth = size.width;
                lastHeight = size.height;

                if (element.resizedAttached) {
                    element.resizedAttached.call(size);
                }
            };

            var onScroll = function() {
                size = getElementSize(element);
                dirty = size.width !== lastWidth || size.height !== lastHeight;

                if (dirty && !rafId) {
                    rafId = requestAnimationFrame(onResized);
                }

                reset();
            };

            var addEvent = function(el, name, cb) {
                if (el.attachEvent) {
                    el.attachEvent('on' + name, cb);
                } else {
                    el.addEventListener(name, cb);
                }
            };

            addEvent(expand, 'scroll', onScroll);
            addEvent(shrink, 'scroll', onScroll);

            // Fix for custom Elements
            requestAnimationFrame(reset);
        }

        forEachElement(element, function(elem){
            attachResizeEvent(elem, callback);
        });

        this.detach = function(ev) {
            ResizeSensor.detach(element, ev);
        };

        this.reset = function() {
            element.resizeSensor.resetSensor();
        };
    };

    ResizeSensor.reset = function(element) {
        forEachElement(element, function(elem){
            elem.resizeSensor.resetSensor();
        });
    };

    ResizeSensor.detach = function(element, ev) {
        forEachElement(element, function(elem){
            if (!elem) return;
            if(elem.resizedAttached && typeof ev === "function"){
                elem.resizedAttached.remove(ev);
                if(elem.resizedAttached.length()) return;
            }
            if (elem.resizeSensor) {
                if (elem.contains(elem.resizeSensor)) {
                    elem.removeChild(elem.resizeSensor);
                }
                delete elem.resizeSensor;
                delete elem.resizedAttached;
            }
        });
    };

    if (typeof MutationObserver !== "undefined") {
        var observer = new MutationObserver(function (mutations) {
            for (var i in mutations) {
                if (mutations.hasOwnProperty(i)) {
                    var items = mutations[i].addedNodes;
                    for (var j = 0; j < items.length; j++) {
                        if (items[j].resizeSensor) {
                            ResizeSensor.reset(items[j]);
                        }
                    }
                }
            }
        });

        document.addEventListener("DOMContentLoaded", function (event) {
            observer.observe(document.body, {
                childList: true,
                subtree: true,
            });
        });
    }

    return ResizeSensor;

}));
;
